]> git.dpolakovic.space - halo3-reclaimer/commitdiff
feat: Custom game types extraction implemented master 0.3.0
authorDavid Polakovic
Tue, 3 Nov 2026 15:06:11 +0000 (16:06 +0100)
committerDavid Polakovic
Tue, 3 Nov 2026 15:10:12 +0000 (16:10 +0100)
-new class GameModeReclaimer.java
-updated documentation, README and build.xml for version changes
-changed Error numbering

64 files changed:
README.txt
build.xml
build/Arbiter$Kind.class
build/Arbiter.class
build/Cortana.class
build/ErrorHandler.class
build/GameModeReclaimer.class [new file with mode: 0644]
build/GraphicalUI$BrowseRawActionListener$1.class
build/GraphicalUI$BrowseRawActionListener.class
build/GraphicalUI$BrowseSaveActionListener.class
build/GraphicalUI$ReclaimActionListener$1.class
build/GraphicalUI$ReclaimActionListener.class
build/GraphicalUI.class
build/MapReclaimer$1.class [deleted file]
build/MapReclaimer$ContentInfo.class [new file with mode: 0644]
build/MapReclaimer$MapInfo.class [deleted file]
build/MapReclaimer$StfsEntry.class
build/MapReclaimer$StfsPackage.class
build/MapReclaimer.class
build/ScreenshotReclaimer.class
build/TerminalUI.class
doc/Arbiter.html
doc/Cortana.html
doc/ErrorHandler.html
doc/GameModeReclaimer.html [new file with mode: 0644]
doc/GraphicalUI.html
doc/Main.html
doc/MapReclaimer.html
doc/ScreenshotReclaimer.html
doc/TerminalUI.html
doc/allclasses-frame.html
doc/allclasses-noframe.html
doc/class-use/Arbiter.html
doc/class-use/Cortana.html
doc/class-use/ErrorHandler.html
doc/class-use/GameModeReclaimer.html [new file with mode: 0644]
doc/class-use/GraphicalUI.html
doc/class-use/Main.html
doc/class-use/MapReclaimer.html
doc/class-use/ScreenshotReclaimer.html
doc/class-use/TerminalUI.html
doc/constant-values.html
doc/deprecated-list.html
doc/help-doc.html
doc/index-all.html
doc/index.html
doc/overview-summary.html
doc/overview-tree.html
doc/package-frame.html
doc/package-summary.html
doc/package-tree.html
doc/package-use.html
doc/serialized-form.html
doc/user-manual-editable.odt
doc/user-manual.pdf
halo3-reclaimer.jar
src/Arbiter.java
src/ErrorHandler.java
src/GameModeReclaimer.java [new file with mode: 0644]
src/GraphicalUI.java
src/Main.java
src/MapReclaimer.java
src/ScreenshotReclaimer.java
src/TerminalUI.java

index 2b6ef61dd55b7d1dd39881a52e052268c07fe7f2..1d3ec4944def62b525f94ddc54d80a67d3fe22c7 100755 (executable)
@@ -8,26 +8,27 @@ ABOUT:
 Today it is almost impossible to get you player data
 like screenshots or Forge map variants from classic 
 Xbox 360 Halo 3 to you PC. This program solves this 
 Today it is almost impossible to get you player data
 like screenshots or Forge map variants from classic 
 Xbox 360 Halo 3 to you PC. This program solves this 
-problem. The reclaimer currently exports:
+problem. The Reclaimer currently exports:
 - player screenshots
 - Forge map variants
 - player screenshots
 - Forge map variants
+- Custom game types
 
 
 
 BUILDING FORM SOURCE:
 
 
 
 BUILDING FORM SOURCE:
-  Even tough I am not a supporter of distributing software
+  Even though I am not a supporter of distributing software
   in already built form, I do so for the comfort of target
   audience of this specific program. So unless you want to
   change some suff in the code, YOU CAN SKIP THIS PROCEDURE.
   
   Otherwise, there is a build.xml file configured for your
   in already built form, I do so for the comfort of target
   audience of this specific program. So unless you want to
   change some suff in the code, YOU CAN SKIP THIS PROCEDURE.
   
   Otherwise, there is a build.xml file configured for your
-  (and mine) convinience which requires build tool Apache Ant.
+  (and mine) convinience, which requires build tool Apache Ant.
   https://ant.apache.org/index.html
   
   To build the program:
   1. Install Ant 
   2. Inspect the 'build.xml' in root directory
   https://ant.apache.org/index.html
   
   To build the program:
   1. Install Ant 
   2. Inspect the 'build.xml' in root directory
-  3. Inspect the code in src to see what it does
+  3. Inspect the code in src/ to see what it does
   4. Then simply run:
            ant all
 
   4. Then simply run:
            ant all
 
@@ -49,7 +50,7 @@ HOW TO RUN:
 
 
 HOW TO USE
 
 
 HOW TO USE
-  See user manual in 'Doc/user-manual.pdf'.
+  See user manual in doc/user-manual.pdf.
 
 
 
 
 
 
@@ -73,3 +74,10 @@ CONTACT:
 RELEASE NOTES:
   0.1 - Screenshots extracting 
   0.2 - Forge maps extracting
 RELEASE NOTES:
   0.1 - Screenshots extracting 
   0.2 - Forge maps extracting
+  0.3 - Custom game types extracting
+
+
+
+TO DO:
+  1.0 - Films extracting
+  1.? - Campaign saves extracting 
index 820bf8642aecd841c7a98425754356a309833615..698ca1842bceddc5c4f491653efb0df0bc4cc5e0 100644 (file)
--- a/build.xml
+++ b/build.xml
@@ -73,8 +73,8 @@
                  version="true"
                  use="true"
                  windowtitle="Halo 3 Reclaimer"
                  version="true"
                  use="true"
                  windowtitle="Halo 3 Reclaimer"
-                 doctitle="Halo 3 Reclaimer - File extractor from Xbox 360 version of Halo 3"
-                 bottom="Copyright 2025. Documentation licensed under GFDL v1.3."
+                 doctitle="Halo 3 Reclaimer - player data extractor from Xbox 360 version of Halo 3"
+                 bottom="Copyright 2025, 2026. Documentation licensed under GFDL v1.3."
                  overview="overview.html">
             <fileset dir="${src.dir}">
                 <include name="*.java"/>
                  overview="overview.html">
             <fileset dir="${src.dir}">
                 <include name="*.java"/>
@@ -90,4 +90,4 @@
     
     <!-- Build everything -->
     <target name="all" depends="clean,jar,javadoc"/>
     
     <!-- Build everything -->
     <target name="all" depends="clean,jar,javadoc"/>
-</project>
\ No newline at end of file
+</project>
index 90195c38520cdb74320faee6e1506f2f6f7fd23e..13e877d552da6a44232eac4a83b8d4fcd8c57b5d 100644 (file)
Binary files a/build/Arbiter$Kind.class and b/build/Arbiter$Kind.class differ
index 17cab3cbbfd4cb706909d8c7a29851076ff0da2b..dc7d0b03ac5ddb92a04da1e35d14fbc7b8bc94ac 100644 (file)
Binary files a/build/Arbiter.class and b/build/Arbiter.class differ
index 54caa7852e59ac5449d7e60b7f2154ff804a2a3a..8513d645d1ee7806535a1fb4253b2c318de49805 100644 (file)
Binary files a/build/Cortana.class and b/build/Cortana.class differ
index e0c5fce8d2d422810fa03f47874212eee7cd8ae8..365a24fe63895f1891e31a0f1488aec1747b910a 100644 (file)
Binary files a/build/ErrorHandler.class and b/build/ErrorHandler.class differ
diff --git a/build/GameModeReclaimer.class b/build/GameModeReclaimer.class
new file mode 100644 (file)
index 0000000..d874a20
Binary files /dev/null and b/build/GameModeReclaimer.class differ
index 11b2217d785545948fbdc1cdc4f2c4b08dcd633f..0c1173c60414d1a3a457845e2f8a184dd30d31c7 100644 (file)
Binary files a/build/GraphicalUI$BrowseRawActionListener$1.class and b/build/GraphicalUI$BrowseRawActionListener$1.class differ
index 5245384ed42d8cab43eec87e13837d12ee1d84ab..c092a72d61227ab415c797631797787583788342 100644 (file)
Binary files a/build/GraphicalUI$BrowseRawActionListener.class and b/build/GraphicalUI$BrowseRawActionListener.class differ
index 18637bffea245988d83bb2134c12ff45b648548b..7e9f61bf9e681641ca714765a56ba261ca8381df 100644 (file)
Binary files a/build/GraphicalUI$BrowseSaveActionListener.class and b/build/GraphicalUI$BrowseSaveActionListener.class differ
index db8c726c1cf3a1152bb4fe4deb42f16cb7a173d0..e2c5b56cc522536744f47b1196c99b3be1fa39a1 100644 (file)
Binary files a/build/GraphicalUI$ReclaimActionListener$1.class and b/build/GraphicalUI$ReclaimActionListener$1.class differ
index 8756b541f747fae9c4fa0f3daf51065939126124..6613277f223fadffa69578613fbac81be4b88297 100644 (file)
Binary files a/build/GraphicalUI$ReclaimActionListener.class and b/build/GraphicalUI$ReclaimActionListener.class differ
index 4203d4e4a68edbc1929677b31920d0fd4bafcea8..2dc95223b17d51c4d0570111f1545a1639af28b1 100644 (file)
Binary files a/build/GraphicalUI.class and b/build/GraphicalUI.class differ
diff --git a/build/MapReclaimer$1.class b/build/MapReclaimer$1.class
deleted file mode 100644 (file)
index 7c73151..0000000
Binary files a/build/MapReclaimer$1.class and /dev/null differ
diff --git a/build/MapReclaimer$ContentInfo.class b/build/MapReclaimer$ContentInfo.class
new file mode 100644 (file)
index 0000000..a292b1f
Binary files /dev/null and b/build/MapReclaimer$ContentInfo.class differ
diff --git a/build/MapReclaimer$MapInfo.class b/build/MapReclaimer$MapInfo.class
deleted file mode 100644 (file)
index 677ce76..0000000
Binary files a/build/MapReclaimer$MapInfo.class and /dev/null differ
index a2384258383deabe0c5135555d2b121ab5323219..4e78972773c9a15bd94f13d19e18a82177e4c6f1 100644 (file)
Binary files a/build/MapReclaimer$StfsEntry.class and b/build/MapReclaimer$StfsEntry.class differ
index 12c5e3a3efae135a3ee024a7371eb3ced65b463b..93e7c2cb82c79c654d3121d83361dd0e42ebe852 100644 (file)
Binary files a/build/MapReclaimer$StfsPackage.class and b/build/MapReclaimer$StfsPackage.class differ
index 65cfeb1829344f32b569909d1b76628d620fcc29..8c72a47d2d5b60fad5011fa94363e684328660a5 100644 (file)
Binary files a/build/MapReclaimer.class and b/build/MapReclaimer.class differ
index dff2b10087e347c9beb126aff2ced964bf0f16ec..8d1c3c10413c7e5b0e8956f1b3b5bf2bd4432173 100644 (file)
Binary files a/build/ScreenshotReclaimer.class and b/build/ScreenshotReclaimer.class differ
index 602d2c331f84ae04277f4f3ee59a328c8556229b..69b0969c08a8b68cf696bcd2f2066539eedd74aa 100644 (file)
Binary files a/build/TerminalUI.class and b/build/TerminalUI.class differ
index 0e1d10dcc2b127a0ea611ab3ce1793684a6cf10a..7e2d7e48cb89497e9ec8aadbd5b17d3eb4f4c25b 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Arbiter (Halo 3 Reclaimer)</title>
 <title>Arbiter (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
@@ -118,18 +118,20 @@ extends java.lang.Object</pre>
  Cortana first, which copies the Halo 3 files out of the Xbox file system;
  other files on the stick are ignored then.</p>
 
  Cortana first, which copies the Halo 3 files out of the Xbox file system;
  other files on the stick are ignored then.</p>
 
- <p>Halo 3 keeps every screenshot and every Forge map in its own package,
- so the Arbiter looks inside each file: a package holding
+ <p>Halo 3 keeps every screenshot, Forge map and custom game type in its own
+ package, so the Arbiter looks inside each file: a package holding
  <code>screen.shot</code> is a screenshot, one holding <code>sandbox.map</code>
  <code>screen.shot</code> is a screenshot, one holding <code>sandbox.map</code>
- is a map. Screenshots are checked first, then maps. Anything else is skipped.
- Only when the content says nothing (DDS, TGA, raw textures) the file name
- decides, like in version 1.0.</p>
+ is a map and one holding <code>variant.slayer</code>, <code>variant.ctf</code>...
+ is a game mode. Screenshots are checked first, then maps, then game modes.
+ Anything else is skipped. Only when the content says nothing (DDS, TGA,
+ raw textures) the file name decides, like in version 1.0.</p>
 
 
- <p>Results go to two folders in the output directory:
- <code>screenshots-reclaimed</code> and <code>maps-reclaimed</code>.</p></div>
+ <p>Results go to <code>halo-3-reclaimed-files</code> in the output directory,
+ sorted into <code>screenshots</code>, <code>maps</code> and <code>game-modes</code>.
+ Files are named after their in-game names, see <a href="Arbiter.html#saveFiles-java.io.File-java.lang.String-java.lang.String-java.util.Map-"><code>saveFiles(java.io.File, java.lang.String, java.lang.String, java.util.Map&lt;java.lang.String, byte[]&gt;)</code></a>.</p></div>
 <dl>
 <dt><span class="simpleTagLabel">Version:</span></dt>
 <dl>
 <dt><span class="simpleTagLabel">Version:</span></dt>
-<dd>1.0</dd>
+<dd>1.1</dd>
 </dl>
 </li>
 </ul>
 </dl>
 </li>
 </ul>
@@ -151,8 +153,16 @@ extends java.lang.Object</pre>
 </tr>
 <tr class="altColor">
 <td class="colFirst"><code>static java.lang.String</code></td>
 </tr>
 <tr class="altColor">
 <td class="colFirst"><code>static java.lang.String</code></td>
+<td class="colLast"><code><span class="memberNameLink"><a href="Arbiter.html#GAME_MODES_DIR">GAME_MODES_DIR</a></span></code>&nbsp;</td>
+</tr>
+<tr class="rowColor">
+<td class="colFirst"><code>static java.lang.String</code></td>
 <td class="colLast"><code><span class="memberNameLink"><a href="Arbiter.html#MAPS_DIR">MAPS_DIR</a></span></code>&nbsp;</td>
 </tr>
 <td class="colLast"><code><span class="memberNameLink"><a href="Arbiter.html#MAPS_DIR">MAPS_DIR</a></span></code>&nbsp;</td>
 </tr>
+<tr class="altColor">
+<td class="colFirst"><code>static java.lang.String</code></td>
+<td class="colLast"><code><span class="memberNameLink"><a href="Arbiter.html#OUTPUT_DIR">OUTPUT_DIR</a></span></code>&nbsp;</td>
+</tr>
 <tr class="rowColor">
 <td class="colFirst"><code>static java.lang.String</code></td>
 <td class="colLast"><code><span class="memberNameLink"><a href="Arbiter.html#SCREENSHOTS_DIR">SCREENSHOTS_DIR</a></span></code>&nbsp;</td>
 <tr class="rowColor">
 <td class="colFirst"><code>static java.lang.String</code></td>
 <td class="colLast"><code><span class="memberNameLink"><a href="Arbiter.html#SCREENSHOTS_DIR">SCREENSHOTS_DIR</a></span></code>&nbsp;</td>
@@ -193,7 +203,7 @@ extends java.lang.Object</pre>
 <td class="colFirst"><code>static java.lang.String</code></td>
 <td class="colLast"><code><span class="memberNameLink"><a href="Arbiter.html#processFile-java.io.File-java.io.File-">processFile</a></span>(java.io.File&nbsp;input,
            java.io.File&nbsp;outputDir)</code>
 <td class="colFirst"><code>static java.lang.String</code></td>
 <td class="colLast"><code><span class="memberNameLink"><a href="Arbiter.html#processFile-java.io.File-java.io.File-">processFile</a></span>(java.io.File&nbsp;input,
            java.io.File&nbsp;outputDir)</code>
-<div class="block">Reclaims all screenshots and maps found in a file or folder.</div>
+<div class="block">Reclaims all screenshots, maps and game modes found in a file or folder.</div>
 </td>
 </tr>
 </table>
 </td>
 </tr>
 </table>
@@ -218,6 +228,19 @@ extends java.lang.Object</pre>
 <!--   -->
 </a>
 <h3>Field Detail</h3>
 <!--   -->
 </a>
 <h3>Field Detail</h3>
+<a name="OUTPUT_DIR">
+<!--   -->
+</a>
+<ul class="blockList">
+<li class="blockList">
+<h4>OUTPUT_DIR</h4>
+<pre>public static final&nbsp;java.lang.String OUTPUT_DIR</pre>
+<dl>
+<dt><span class="seeLabel">See Also:</span></dt>
+<dd><a href="constant-values.html#Arbiter.OUTPUT_DIR">Constant Field Values</a></dd>
+</dl>
+</li>
+</ul>
 <a name="SCREENSHOTS_DIR">
 <!--   -->
 </a>
 <a name="SCREENSHOTS_DIR">
 <!--   -->
 </a>
@@ -234,7 +257,7 @@ extends java.lang.Object</pre>
 <a name="MAPS_DIR">
 <!--   -->
 </a>
 <a name="MAPS_DIR">
 <!--   -->
 </a>
-<ul class="blockListLast">
+<ul class="blockList">
 <li class="blockList">
 <h4>MAPS_DIR</h4>
 <pre>public static final&nbsp;java.lang.String MAPS_DIR</pre>
 <li class="blockList">
 <h4>MAPS_DIR</h4>
 <pre>public static final&nbsp;java.lang.String MAPS_DIR</pre>
@@ -244,6 +267,19 @@ extends java.lang.Object</pre>
 </dl>
 </li>
 </ul>
 </dl>
 </li>
 </ul>
+<a name="GAME_MODES_DIR">
+<!--   -->
+</a>
+<ul class="blockListLast">
+<li class="blockList">
+<h4>GAME_MODES_DIR</h4>
+<pre>public static final&nbsp;java.lang.String GAME_MODES_DIR</pre>
+<dl>
+<dt><span class="seeLabel">See Also:</span></dt>
+<dd><a href="constant-values.html#Arbiter.GAME_MODES_DIR">Constant Field Values</a></dd>
+</dl>
+</li>
+</ul>
 </li>
 </ul>
 <!-- ========= CONSTRUCTOR DETAIL ======== -->
 </li>
 </ul>
 <!-- ========= CONSTRUCTOR DETAIL ======== -->
@@ -278,10 +314,10 @@ extends java.lang.Object</pre>
 <pre>public static&nbsp;java.lang.String&nbsp;processFile(java.io.File&nbsp;input,
                                            java.io.File&nbsp;outputDir)
                                     throws java.lang.Exception</pre>
 <pre>public static&nbsp;java.lang.String&nbsp;processFile(java.io.File&nbsp;input,
                                            java.io.File&nbsp;outputDir)
                                     throws java.lang.Exception</pre>
-<div class="block">Reclaims all screenshots and maps found in a file or folder.</div>
+<div class="block">Reclaims all screenshots, maps and game modes found in a file or folder.</div>
 <dl>
 <dt><span class="returnLabel">Returns:</span></dt>
 <dl>
 <dt><span class="returnLabel">Returns:</span></dt>
-<dd>what was reclaimed, for example "Reclaimed 2 pictures and 1 map"</dd>
+<dd>what was reclaimed, for example "Reclaimed 2 pictures, 1 map and 3 game modes"</dd>
 <dt><span class="throwsLabel">Throws:</span></dt>
 <dd><code>java.lang.Exception</code> - if nothing could be reclaimed</dd>
 </dl>
 <dt><span class="throwsLabel">Throws:</span></dt>
 <dd><code>java.lang.Exception</code> - if nothing could be reclaimed</dd>
 </dl>
@@ -357,6 +393,6 @@ extends java.lang.Object</pre>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 65665a593773c6594f04f466929b2e250fa6bf9f..14b95976b3affcd6166de82500aa786734ac33e0 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Cortana (Halo 3 Reclaimer)</title>
 <title>Cortana (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
@@ -331,6 +331,6 @@ extends java.lang.Object</pre>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 6ec8e75cd38e47d0108762b6380699813c56b8aa..a5a2319f36011756c80a72e84966ea1c6ec661fb 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>ErrorHandler (Halo 3 Reclaimer)</title>
 <title>ErrorHandler (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
@@ -51,7 +51,7 @@ var activeTableTab = "activeTableTab";
 <div class="subNav">
 <ul class="navList">
 <li><a href="Cortana.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Prev&nbsp;Class</span></a></li>
 <div class="subNav">
 <ul class="navList">
 <li><a href="Cortana.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Prev&nbsp;Class</span></a></li>
-<li><a href="GraphicalUI.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
+<li><a href="GameModeReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
 </ul>
 <ul class="navList">
 <li><a href="index.html?ErrorHandler.html" target="_top">Frames</a></li>
 </ul>
 <ul class="navList">
 <li><a href="index.html?ErrorHandler.html" target="_top">Frames</a></li>
@@ -116,7 +116,7 @@ extends java.lang.Object</pre>
  <p>This class just stores error messages and prints them when needed.</p></div>
 <dl>
 <dt><span class="simpleTagLabel">Version:</span></dt>
  <p>This class just stores error messages and prints them when needed.</p></div>
 <dl>
 <dt><span class="simpleTagLabel">Version:</span></dt>
-<dd>1.0</dd>
+<dd>1.1</dd>
 </dl>
 </li>
 </ul>
 </dl>
 </li>
 </ul>
@@ -431,7 +431,7 @@ extends java.lang.Object</pre>
 <div class="subNav">
 <ul class="navList">
 <li><a href="Cortana.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Prev&nbsp;Class</span></a></li>
 <div class="subNav">
 <ul class="navList">
 <li><a href="Cortana.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Prev&nbsp;Class</span></a></li>
-<li><a href="GraphicalUI.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
+<li><a href="GameModeReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
 </ul>
 <ul class="navList">
 <li><a href="index.html?ErrorHandler.html" target="_top">Frames</a></li>
 </ul>
 <ul class="navList">
 <li><a href="index.html?ErrorHandler.html" target="_top">Frames</a></li>
@@ -471,6 +471,6 @@ extends java.lang.Object</pre>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
diff --git a/doc/GameModeReclaimer.html b/doc/GameModeReclaimer.html
new file mode 100644 (file)
index 0000000..9fd2a79
--- /dev/null
@@ -0,0 +1,312 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<!-- NewPage -->
+<html lang="en">
+<head>
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
+<title>GameModeReclaimer (Halo 3 Reclaimer)</title>
+<meta name="date" content="2026-11-03">
+<link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
+<script type="text/javascript" src="script.js"></script>
+</head>
+<body>
+<script type="text/javascript"><!--
+    try {
+        if (location.href.indexOf('is-external=true') == -1) {
+            parent.document.title="GameModeReclaimer (Halo 3 Reclaimer)";
+        }
+    }
+    catch(err) {
+    }
+//-->
+var methods = {"i0":10};
+var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"]};
+var altColor = "altColor";
+var rowColor = "rowColor";
+var tableTab = "tableTab";
+var activeTableTab = "activeTableTab";
+</script>
+<noscript>
+<div>JavaScript is disabled on your browser.</div>
+</noscript>
+<!-- ========= START OF TOP NAVBAR ======= -->
+<div class="topNav"><a name="navbar.top">
+<!--   -->
+</a>
+<div class="skipNav"><a href="#skip.navbar.top" title="Skip navigation links">Skip navigation links</a></div>
+<a name="navbar.top.firstrow">
+<!--   -->
+</a>
+<ul class="navList" title="Navigation">
+<li><a href="overview-summary.html">Overview</a></li>
+<li><a href="package-summary.html">Package</a></li>
+<li class="navBarCell1Rev">Class</li>
+<li><a href="class-use/GameModeReclaimer.html">Use</a></li>
+<li><a href="package-tree.html">Tree</a></li>
+<li><a href="deprecated-list.html">Deprecated</a></li>
+<li><a href="index-all.html">Index</a></li>
+<li><a href="help-doc.html">Help</a></li>
+</ul>
+<div class="aboutLanguage"><b>Halo 3 Reclaimer</b></div>
+</div>
+<div class="subNav">
+<ul class="navList">
+<li><a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Prev&nbsp;Class</span></a></li>
+<li><a href="GraphicalUI.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
+</ul>
+<ul class="navList">
+<li><a href="index.html?GameModeReclaimer.html" target="_top">Frames</a></li>
+<li><a href="GameModeReclaimer.html" target="_top">No&nbsp;Frames</a></li>
+</ul>
+<ul class="navList" id="allclasses_navbar_top">
+<li><a href="allclasses-noframe.html">All&nbsp;Classes</a></li>
+</ul>
+<div>
+<script type="text/javascript"><!--
+  allClassesLink = document.getElementById("allclasses_navbar_top");
+  if(window==top) {
+    allClassesLink.style.display = "block";
+  }
+  else {
+    allClassesLink.style.display = "none";
+  }
+  //-->
+</script>
+</div>
+<div>
+<ul class="subNavList">
+<li>Summary:&nbsp;</li>
+<li>Nested&nbsp;|&nbsp;</li>
+<li>Field&nbsp;|&nbsp;</li>
+<li><a href="#constructor.summary">Constr</a>&nbsp;|&nbsp;</li>
+<li><a href="#method.summary">Method</a></li>
+</ul>
+<ul class="subNavList">
+<li>Detail:&nbsp;</li>
+<li>Field&nbsp;|&nbsp;</li>
+<li><a href="#constructor.detail">Constr</a>&nbsp;|&nbsp;</li>
+<li><a href="#method.detail">Method</a></li>
+</ul>
+</div>
+<a name="skip.navbar.top">
+<!--   -->
+</a></div>
+<!-- ========= END OF TOP NAVBAR ========= -->
+<!-- ======== START OF CLASS DATA ======== -->
+<div class="header">
+<h2 title="Class GameModeReclaimer" class="title">Class GameModeReclaimer</h2>
+</div>
+<div class="contentContainer">
+<ul class="inheritance">
+<li>java.lang.Object</li>
+<li>
+<ul class="inheritance">
+<li>GameModeReclaimer</li>
+</ul>
+</li>
+</ul>
+<div class="description">
+<ul class="blockList">
+<li class="blockList">
+<hr>
+<br>
+<pre>public class <span class="typeNameLabel">GameModeReclaimer</span>
+extends java.lang.Object</pre>
+<div class="block">Core extraction engine for custom game types (game modes).
+
+ <p>Halo 3 saves every custom game type as its own Xbox 360 package, just
+ like the Forge maps. The package holds one file named after the game's
+ rules, for example <code>variant.slayer</code> or <code>variant.ctf</code>.
+ That file is the game mode itself, all of its settings, stored in Bungie's
+ BLF format. The package and BLF readers are shared with MapReclaimer.</p>
+
+ <p>Supported input formats:</p>
+ <ul>
+   <li>STFS container files holding a Halo 3 game type</li>
+   <li>already extracted <code>variant.*</code> files (BLF)</li>
+ </ul>
+
+ <p>Two files are saved, named after the game mode (see Arbiter.saveFiles):</p>
+ <ul>
+   <li><code>team_snipers.slayer</code>: byte for byte the file the console
+       keeps inside the package, its ending tells the game type</li>
+   <li><code>team_snipers.package</code>: the untouched package, which tools
+       like Horizon or Velocity can sign for another player's profile
+       (only when the input was a package)</li>
+ </ul></div>
+<dl>
+<dt><span class="simpleTagLabel">Version:</span></dt>
+<dd>1.0</dd>
+</dl>
+</li>
+</ul>
+</div>
+<div class="summary">
+<ul class="blockList">
+<li class="blockList">
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+<ul class="blockList">
+<li class="blockList"><a name="constructor.summary">
+<!--   -->
+</a>
+<h3>Constructor Summary</h3>
+<table class="memberSummary" border="0" cellpadding="3" cellspacing="0" summary="Constructor Summary table, listing constructors, and an explanation">
+<caption><span>Constructors</span><span class="tabEnd">&nbsp;</span></caption>
+<tr>
+<th class="colOne" scope="col">Constructor and Description</th>
+</tr>
+<tr class="altColor">
+<td class="colOne"><code><span class="memberNameLink"><a href="GameModeReclaimer.html#GameModeReclaimer--">GameModeReclaimer</a></span>()</code>&nbsp;</td>
+</tr>
+</table>
+</li>
+</ul>
+<!-- ========== METHOD SUMMARY =========== -->
+<ul class="blockList">
+<li class="blockList"><a name="method.summary">
+<!--   -->
+</a>
+<h3>Method Summary</h3>
+<table class="memberSummary" border="0" cellpadding="3" cellspacing="0" summary="Method Summary table, listing methods, and an explanation">
+<caption><span id="t0" class="activeTableTab"><span>All Methods</span><span class="tabEnd">&nbsp;</span></span><span id="t2" class="tableTab"><span><a href="javascript:show(2);">Instance Methods</a></span><span class="tabEnd">&nbsp;</span></span><span id="t4" class="tableTab"><span><a href="javascript:show(8);">Concrete Methods</a></span><span class="tabEnd">&nbsp;</span></span></caption>
+<tr>
+<th class="colFirst" scope="col">Modifier and Type</th>
+<th class="colLast" scope="col">Method and Description</th>
+</tr>
+<tr id="i0" class="altColor">
+<td class="colFirst"><code>void</code></td>
+<td class="colLast"><code><span class="memberNameLink"><a href="GameModeReclaimer.html#extractGameMode-java.io.File-java.io.File-">extractGameMode</a></span>(java.io.File&nbsp;inputFile,
+               java.io.File&nbsp;outputDir)</code>
+<div class="block">Extracts the game mode from an Xbox 360 file and saves it, together with
+ the package it came in, under its in-game name in the output directory.</div>
+</td>
+</tr>
+</table>
+<ul class="blockList">
+<li class="blockList"><a name="methods.inherited.from.class.java.lang.Object">
+<!--   -->
+</a>
+<h3>Methods inherited from class&nbsp;java.lang.Object</h3>
+<code>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait</code></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="details">
+<ul class="blockList">
+<li class="blockList">
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+<ul class="blockList">
+<li class="blockList"><a name="constructor.detail">
+<!--   -->
+</a>
+<h3>Constructor Detail</h3>
+<a name="GameModeReclaimer--">
+<!--   -->
+</a>
+<ul class="blockListLast">
+<li class="blockList">
+<h4>GameModeReclaimer</h4>
+<pre>public&nbsp;GameModeReclaimer()</pre>
+</li>
+</ul>
+</li>
+</ul>
+<!-- ============ METHOD DETAIL ========== -->
+<ul class="blockList">
+<li class="blockList"><a name="method.detail">
+<!--   -->
+</a>
+<h3>Method Detail</h3>
+<a name="extractGameMode-java.io.File-java.io.File-">
+<!--   -->
+</a>
+<ul class="blockListLast">
+<li class="blockList">
+<h4>extractGameMode</h4>
+<pre>public&nbsp;void&nbsp;extractGameMode(java.io.File&nbsp;inputFile,
+                            java.io.File&nbsp;outputDir)
+                     throws java.lang.Exception</pre>
+<div class="block">Extracts the game mode from an Xbox 360 file and saves it, together with
+ the package it came in, under its in-game name in the output directory.</div>
+<dl>
+<dt><span class="throwsLabel">Throws:</span></dt>
+<dd><code>java.lang.Exception</code> - if the file cannot be read, is not a Halo 3 game mode,
+                   or is damaged</dd>
+</dl>
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+<!-- ========= END OF CLASS DATA ========= -->
+<!-- ======= START OF BOTTOM NAVBAR ====== -->
+<div class="bottomNav"><a name="navbar.bottom">
+<!--   -->
+</a>
+<div class="skipNav"><a href="#skip.navbar.bottom" title="Skip navigation links">Skip navigation links</a></div>
+<a name="navbar.bottom.firstrow">
+<!--   -->
+</a>
+<ul class="navList" title="Navigation">
+<li><a href="overview-summary.html">Overview</a></li>
+<li><a href="package-summary.html">Package</a></li>
+<li class="navBarCell1Rev">Class</li>
+<li><a href="class-use/GameModeReclaimer.html">Use</a></li>
+<li><a href="package-tree.html">Tree</a></li>
+<li><a href="deprecated-list.html">Deprecated</a></li>
+<li><a href="index-all.html">Index</a></li>
+<li><a href="help-doc.html">Help</a></li>
+</ul>
+<div class="aboutLanguage"><i>File extractor from Xbox 360 version of Halo 3</i></div>
+</div>
+<div class="subNav">
+<ul class="navList">
+<li><a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Prev&nbsp;Class</span></a></li>
+<li><a href="GraphicalUI.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
+</ul>
+<ul class="navList">
+<li><a href="index.html?GameModeReclaimer.html" target="_top">Frames</a></li>
+<li><a href="GameModeReclaimer.html" target="_top">No&nbsp;Frames</a></li>
+</ul>
+<ul class="navList" id="allclasses_navbar_bottom">
+<li><a href="allclasses-noframe.html">All&nbsp;Classes</a></li>
+</ul>
+<div>
+<script type="text/javascript"><!--
+  allClassesLink = document.getElementById("allclasses_navbar_bottom");
+  if(window==top) {
+    allClassesLink.style.display = "block";
+  }
+  else {
+    allClassesLink.style.display = "none";
+  }
+  //-->
+</script>
+</div>
+<div>
+<ul class="subNavList">
+<li>Summary:&nbsp;</li>
+<li>Nested&nbsp;|&nbsp;</li>
+<li>Field&nbsp;|&nbsp;</li>
+<li><a href="#constructor.summary">Constr</a>&nbsp;|&nbsp;</li>
+<li><a href="#method.summary">Method</a></li>
+</ul>
+<ul class="subNavList">
+<li>Detail:&nbsp;</li>
+<li>Field&nbsp;|&nbsp;</li>
+<li><a href="#constructor.detail">Constr</a>&nbsp;|&nbsp;</li>
+<li><a href="#method.detail">Method</a></li>
+</ul>
+</div>
+<a name="skip.navbar.bottom">
+<!--   -->
+</a></div>
+<!-- ======== END OF BOTTOM NAVBAR ======= -->
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
+</body>
+</html>
index 94f7b5d9ec3e796e6f58108a8d7ac8b7e63e223f..25d08c7801a5928782535938aeb06ae3faa17dfa 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>GraphicalUI (Halo 3 Reclaimer)</title>
 <title>GraphicalUI (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
@@ -50,7 +50,7 @@ var activeTableTab = "activeTableTab";
 </div>
 <div class="subNav">
 <ul class="navList">
 </div>
 <div class="subNav">
 <ul class="navList">
-<li><a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Prev&nbsp;Class</span></a></li>
+<li><a href="GameModeReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Prev&nbsp;Class</span></a></li>
 <li><a href="Main.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
 </ul>
 <ul class="navList">
 <li><a href="Main.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
 </ul>
 <ul class="navList">
@@ -146,7 +146,7 @@ extends javax.swing.JFrame</pre>
  logic for the application's windowed interface</p></div>
 <dl>
 <dt><span class="simpleTagLabel">Version:</span></dt>
  logic for the application's windowed interface</p></div>
 <dl>
 <dt><span class="simpleTagLabel">Version:</span></dt>
-<dd>1.1</dd>
+<dd>1.2</dd>
 <dt><span class="seeLabel">See Also:</span></dt>
 <dd><a href="serialized-form.html#GraphicalUI">Serialized Form</a></dd>
 </dl>
 <dt><span class="seeLabel">See Also:</span></dt>
 <dd><a href="serialized-form.html#GraphicalUI">Serialized Form</a></dd>
 </dl>
@@ -400,7 +400,7 @@ extends javax.swing.JFrame</pre>
 </div>
 <div class="subNav">
 <ul class="navList">
 </div>
 <div class="subNav">
 <ul class="navList">
-<li><a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Prev&nbsp;Class</span></a></li>
+<li><a href="GameModeReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Prev&nbsp;Class</span></a></li>
 <li><a href="Main.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
 </ul>
 <ul class="navList">
 <li><a href="Main.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
 </ul>
 <ul class="navList">
@@ -441,6 +441,6 @@ extends javax.swing.JFrame</pre>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 711b5d2dd7a0bcf747e4d1f1a565152915afd98e..a357656aa119206df13d3e774d2043977ef34206 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Main (Halo 3 Reclaimer)</title>
 <title>Main (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
@@ -276,6 +276,6 @@ extends java.lang.Object</pre>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index e50c908043a6476baf914933396f27300af91bca..28cafee1da548d4b8ee62b7e7bed4f4302eb650f 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>MapReclaimer (Halo 3 Reclaimer)</title>
 <title>MapReclaimer (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
@@ -123,7 +123,8 @@ extends java.lang.Object</pre>
  <p>Unlike screenshots, the map has no magic bytes worth scanning for,
  so this class reads the STFS file system properly: it walks the file
  table, follows the block chain of <code>sandbox.map</code> and checks
  <p>Unlike screenshots, the map has no magic bytes worth scanning for,
  so this class reads the STFS file system properly: it walks the file
  table, follows the block chain of <code>sandbox.map</code> and checks
- each block against the package hash tables.</p>
+ each block against the package hash tables. These package and BLF readers
+ are shared with the other reclaimers.</p>
 
  <p>Supported input formats:</p>
  <ul>
 
  <p>Supported input formats:</p>
  <ul>
@@ -131,11 +132,17 @@ extends java.lang.Object</pre>
    <li>already extracted <code>sandbox.map</code> files (BLF)</li>
  </ul>
 
    <li>already extracted <code>sandbox.map</code> files (BLF)</li>
  </ul>
 
- <p>The saved file is not converted in any way. It is byte for byte the
- <code>sandbox.map</code> the console keeps inside the package.</p></div>
+ <p>Two files are saved, named after the map (see Arbiter.saveFiles):</p>
+ <ul>
+   <li><code>guardian_infection.map</code>: byte for byte the
+       <code>sandbox.map</code> the console keeps inside the package</li>
+   <li><code>guardian_infection.package</code>: the untouched package, which
+       tools like Horizon or Velocity can sign for another player's profile
+       (only when the input was a package)</li>
+ </ul></div>
 <dl>
 <dt><span class="simpleTagLabel">Version:</span></dt>
 <dl>
 <dt><span class="simpleTagLabel">Version:</span></dt>
-<dd>1.0</dd>
+<dd>1.1</dd>
 </dl>
 </li>
 </ul>
 </dl>
 </li>
 </ul>
@@ -176,8 +183,8 @@ extends java.lang.Object</pre>
 <td class="colFirst"><code>void</code></td>
 <td class="colLast"><code><span class="memberNameLink"><a href="MapReclaimer.html#extractMap-java.io.File-java.io.File-">extractMap</a></span>(java.io.File&nbsp;inputFile,
           java.io.File&nbsp;outputDir)</code>
 <td class="colFirst"><code>void</code></td>
 <td class="colLast"><code><span class="memberNameLink"><a href="MapReclaimer.html#extractMap-java.io.File-java.io.File-">extractMap</a></span>(java.io.File&nbsp;inputFile,
           java.io.File&nbsp;outputDir)</code>
-<div class="block">Extracts the Forge map from an Xbox 360 file and saves it as
- &lt;input name&gt;-reclaimed.map in the output directory.</div>
+<div class="block">Extracts the Forge map from an Xbox 360 file and saves it, together with
+ the package it came in, under the map's in-game name in the output directory.</div>
 </td>
 </tr>
 </table>
 </td>
 </tr>
 </table>
@@ -228,8 +235,8 @@ extends java.lang.Object</pre>
 <pre>public&nbsp;void&nbsp;extractMap(java.io.File&nbsp;inputFile,
                        java.io.File&nbsp;outputDir)
                 throws java.lang.Exception</pre>
 <pre>public&nbsp;void&nbsp;extractMap(java.io.File&nbsp;inputFile,
                        java.io.File&nbsp;outputDir)
                 throws java.lang.Exception</pre>
-<div class="block">Extracts the Forge map from an Xbox 360 file and saves it as
- &lt;input name&gt;-reclaimed.map in the output directory.</div>
+<div class="block">Extracts the Forge map from an Xbox 360 file and saves it, together with
+ the package it came in, under the map's in-game name in the output directory.</div>
 <dl>
 <dt><span class="throwsLabel">Throws:</span></dt>
 <dd><code>java.lang.Exception</code> - if the file cannot be read, is not a Halo 3 Forge map,
 <dl>
 <dt><span class="throwsLabel">Throws:</span></dt>
 <dd><code>java.lang.Exception</code> - if the file cannot be read, is not a Halo 3 Forge map,
@@ -307,6 +314,6 @@ extends java.lang.Object</pre>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index f5d6983b3cc57af16f90ac936991adb99d32e7fb..a526616ea0b4c294b5e50fb89514e3e446fa9146 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>ScreenshotReclaimer (Halo 3 Reclaimer)</title>
 <title>ScreenshotReclaimer (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
@@ -133,7 +133,7 @@ extends java.lang.Object</pre>
  </ul></div>
 <dl>
 <dt><span class="simpleTagLabel">Version:</span></dt>
  </ul></div>
 <dl>
 <dt><span class="simpleTagLabel">Version:</span></dt>
-<dd>1.0</dd>
+<dd>1.1</dd>
 </dl>
 </li>
 </ul>
 </dl>
 </li>
 </ul>
@@ -302,6 +302,6 @@ extends java.lang.Object</pre>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index ebfd352d737b7dba3831cba2d82d02803ca9125e..6cc1698ed4b3ac470d86056381ae3172e04affc8 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>TerminalUI (Halo 3 Reclaimer)</title>
 <title>TerminalUI (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
@@ -116,7 +116,7 @@ extends java.lang.Object</pre>
  <p>This class contains all the command-line interface logic.</p></div>
 <dl>
 <dt><span class="simpleTagLabel">Version:</span></dt>
  <p>This class contains all the command-line interface logic.</p></div>
 <dl>
 <dt><span class="simpleTagLabel">Version:</span></dt>
-<dd>1.0</dd>
+<dd>1.2</dd>
 </dl>
 </li>
 </ul>
 </dl>
 </li>
 </ul>
@@ -330,6 +330,6 @@ extends java.lang.Object</pre>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 9b46ad16e7d8ecc2a981025974f90b4ebe69e2d4..e5815c1880ace0adf4fac49b569429b7c7bcd46f 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>All Classes (Halo 3 Reclaimer)</title>
 <title>All Classes (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
@@ -15,6 +15,7 @@
 <li><a href="Arbiter.html" title="class in &lt;Unnamed&gt;" target="classFrame">Arbiter</a></li>
 <li><a href="Cortana.html" title="class in &lt;Unnamed&gt;" target="classFrame">Cortana</a></li>
 <li><a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;" target="classFrame">ErrorHandler</a></li>
 <li><a href="Arbiter.html" title="class in &lt;Unnamed&gt;" target="classFrame">Arbiter</a></li>
 <li><a href="Cortana.html" title="class in &lt;Unnamed&gt;" target="classFrame">Cortana</a></li>
 <li><a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;" target="classFrame">ErrorHandler</a></li>
+<li><a href="GameModeReclaimer.html" title="class in &lt;Unnamed&gt;" target="classFrame">GameModeReclaimer</a></li>
 <li><a href="GraphicalUI.html" title="class in &lt;Unnamed&gt;" target="classFrame">GraphicalUI</a></li>
 <li><a href="Main.html" title="class in &lt;Unnamed&gt;" target="classFrame">Main</a></li>
 <li><a href="MapReclaimer.html" title="class in &lt;Unnamed&gt;" target="classFrame">MapReclaimer</a></li>
 <li><a href="GraphicalUI.html" title="class in &lt;Unnamed&gt;" target="classFrame">GraphicalUI</a></li>
 <li><a href="Main.html" title="class in &lt;Unnamed&gt;" target="classFrame">Main</a></li>
 <li><a href="MapReclaimer.html" title="class in &lt;Unnamed&gt;" target="classFrame">MapReclaimer</a></li>
index 5f89623c308b59c2fba0135217876646d2f8d1d5..6a96ca821856368219714d6eee6a0f88c48c1bc0 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>All Classes (Halo 3 Reclaimer)</title>
 <title>All Classes (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
@@ -15,6 +15,7 @@
 <li><a href="Arbiter.html" title="class in &lt;Unnamed&gt;">Arbiter</a></li>
 <li><a href="Cortana.html" title="class in &lt;Unnamed&gt;">Cortana</a></li>
 <li><a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;">ErrorHandler</a></li>
 <li><a href="Arbiter.html" title="class in &lt;Unnamed&gt;">Arbiter</a></li>
 <li><a href="Cortana.html" title="class in &lt;Unnamed&gt;">Cortana</a></li>
 <li><a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;">ErrorHandler</a></li>
+<li><a href="GameModeReclaimer.html" title="class in &lt;Unnamed&gt;">GameModeReclaimer</a></li>
 <li><a href="GraphicalUI.html" title="class in &lt;Unnamed&gt;">GraphicalUI</a></li>
 <li><a href="Main.html" title="class in &lt;Unnamed&gt;">Main</a></li>
 <li><a href="MapReclaimer.html" title="class in &lt;Unnamed&gt;">MapReclaimer</a></li>
 <li><a href="GraphicalUI.html" title="class in &lt;Unnamed&gt;">GraphicalUI</a></li>
 <li><a href="Main.html" title="class in &lt;Unnamed&gt;">Main</a></li>
 <li><a href="MapReclaimer.html" title="class in &lt;Unnamed&gt;">MapReclaimer</a></li>
index 98b442a7b1c8d10814356efd9711712e7ad412c8..fcc82bc20910d5610af53a4d6da7aba2d307f184 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Uses of Class Arbiter (Halo 3 Reclaimer)</title>
 <title>Uses of Class Arbiter (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index cc96b38d838eb515c0a05bf11813d6ce55273be3..ec081768fecdfa3a6796f08352e507e6a3277bff 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Uses of Class Cortana (Halo 3 Reclaimer)</title>
 <title>Uses of Class Cortana (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 64136f5c3f2477f15ae9d75b5c6bcc94cef6ee9c..b473785bca09584f141024d9f20ff0f11e499c55 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Uses of Class ErrorHandler (Halo 3 Reclaimer)</title>
 <title>Uses of Class ErrorHandler (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
diff --git a/doc/class-use/GameModeReclaimer.html b/doc/class-use/GameModeReclaimer.html
new file mode 100644 (file)
index 0000000..a8e7b96
--- /dev/null
@@ -0,0 +1,127 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<!-- NewPage -->
+<html lang="en">
+<head>
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
+<title>Uses of Class GameModeReclaimer (Halo 3 Reclaimer)</title>
+<meta name="date" content="2026-11-03">
+<link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
+<script type="text/javascript" src="../script.js"></script>
+</head>
+<body>
+<script type="text/javascript"><!--
+    try {
+        if (location.href.indexOf('is-external=true') == -1) {
+            parent.document.title="Uses of Class GameModeReclaimer (Halo 3 Reclaimer)";
+        }
+    }
+    catch(err) {
+    }
+//-->
+</script>
+<noscript>
+<div>JavaScript is disabled on your browser.</div>
+</noscript>
+<!-- ========= START OF TOP NAVBAR ======= -->
+<div class="topNav"><a name="navbar.top">
+<!--   -->
+</a>
+<div class="skipNav"><a href="#skip.navbar.top" title="Skip navigation links">Skip navigation links</a></div>
+<a name="navbar.top.firstrow">
+<!--   -->
+</a>
+<ul class="navList" title="Navigation">
+<li><a href="../overview-summary.html">Overview</a></li>
+<li><a href="../package-summary.html">Package</a></li>
+<li><a href="../GameModeReclaimer.html" title="class in &lt;Unnamed&gt;">Class</a></li>
+<li class="navBarCell1Rev">Use</li>
+<li><a href="../overview-tree.html">Tree</a></li>
+<li><a href="../deprecated-list.html">Deprecated</a></li>
+<li><a href="../index-all.html">Index</a></li>
+<li><a href="../help-doc.html">Help</a></li>
+</ul>
+<div class="aboutLanguage"><b>Halo 3 Reclaimer</b></div>
+</div>
+<div class="subNav">
+<ul class="navList">
+<li>Prev</li>
+<li>Next</li>
+</ul>
+<ul class="navList">
+<li><a href="../index.html?class-use/GameModeReclaimer.html" target="_top">Frames</a></li>
+<li><a href="GameModeReclaimer.html" target="_top">No&nbsp;Frames</a></li>
+</ul>
+<ul class="navList" id="allclasses_navbar_top">
+<li><a href="../allclasses-noframe.html">All&nbsp;Classes</a></li>
+</ul>
+<div>
+<script type="text/javascript"><!--
+  allClassesLink = document.getElementById("allclasses_navbar_top");
+  if(window==top) {
+    allClassesLink.style.display = "block";
+  }
+  else {
+    allClassesLink.style.display = "none";
+  }
+  //-->
+</script>
+</div>
+<a name="skip.navbar.top">
+<!--   -->
+</a></div>
+<!-- ========= END OF TOP NAVBAR ========= -->
+<div class="header">
+<h2 title="Uses of Class GameModeReclaimer" class="title">Uses of Class<br>GameModeReclaimer</h2>
+</div>
+<div class="classUseContainer">No usage of GameModeReclaimer</div>
+<!-- ======= START OF BOTTOM NAVBAR ====== -->
+<div class="bottomNav"><a name="navbar.bottom">
+<!--   -->
+</a>
+<div class="skipNav"><a href="#skip.navbar.bottom" title="Skip navigation links">Skip navigation links</a></div>
+<a name="navbar.bottom.firstrow">
+<!--   -->
+</a>
+<ul class="navList" title="Navigation">
+<li><a href="../overview-summary.html">Overview</a></li>
+<li><a href="../package-summary.html">Package</a></li>
+<li><a href="../GameModeReclaimer.html" title="class in &lt;Unnamed&gt;">Class</a></li>
+<li class="navBarCell1Rev">Use</li>
+<li><a href="../overview-tree.html">Tree</a></li>
+<li><a href="../deprecated-list.html">Deprecated</a></li>
+<li><a href="../index-all.html">Index</a></li>
+<li><a href="../help-doc.html">Help</a></li>
+</ul>
+<div class="aboutLanguage"><i>File extractor from Xbox 360 version of Halo 3</i></div>
+</div>
+<div class="subNav">
+<ul class="navList">
+<li>Prev</li>
+<li>Next</li>
+</ul>
+<ul class="navList">
+<li><a href="../index.html?class-use/GameModeReclaimer.html" target="_top">Frames</a></li>
+<li><a href="GameModeReclaimer.html" target="_top">No&nbsp;Frames</a></li>
+</ul>
+<ul class="navList" id="allclasses_navbar_bottom">
+<li><a href="../allclasses-noframe.html">All&nbsp;Classes</a></li>
+</ul>
+<div>
+<script type="text/javascript"><!--
+  allClassesLink = document.getElementById("allclasses_navbar_bottom");
+  if(window==top) {
+    allClassesLink.style.display = "block";
+  }
+  else {
+    allClassesLink.style.display = "none";
+  }
+  //-->
+</script>
+</div>
+<a name="skip.navbar.bottom">
+<!--   -->
+</a></div>
+<!-- ======== END OF BOTTOM NAVBAR ======= -->
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
+</body>
+</html>
index afbd7efbb002ab6a382b69757ae53dc4a4914a7e..e8f5fa29a2d0776a4ea4fdf0ec6c1f4b38911a0e 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Uses of Class GraphicalUI (Halo 3 Reclaimer)</title>
 <title>Uses of Class GraphicalUI (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 06f5dcabceb3165b888d0f3f0059c7f6b3e99a91..e8e451b04c186d66ea94e04aa320c4d0bf446ffd 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Uses of Class Main (Halo 3 Reclaimer)</title>
 <title>Uses of Class Main (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 90cef7831bcf4cff2f87739839a417c8338bf683..238ac0bd84d5eb32665bf01d69e9aecad032cf91 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Uses of Class MapReclaimer (Halo 3 Reclaimer)</title>
 <title>Uses of Class MapReclaimer (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 94a40f100c0c4cf479b30ec9c46797afbc1baa25..57036fcc874904823b9dfa071abf213c29ad7867 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Uses of Class ScreenshotReclaimer (Halo 3 Reclaimer)</title>
 <title>Uses of Class ScreenshotReclaimer (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 75278bdd1146c50f4ec563ff92b570a4ceb1d359..784c1ee6310b39297ccaa53fbfe68c134626fee3 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Uses of Class TerminalUI (Halo 3 Reclaimer)</title>
 <title>Uses of Class TerminalUI (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">
 <script type="text/javascript" src="../script.js"></script>
 </head>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index ea15a6ea5793f836ae5999551183f2d9ea847ef7..ed9bdabe585b803b244e8debf6210e70ad92c020 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Constant Field Values (Halo 3 Reclaimer)</title>
 <title>Constant Field Values (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 </tr>
 <tbody>
 <tr class="altColor">
 </tr>
 <tbody>
 <tr class="altColor">
+<td class="colFirst"><a name="Arbiter.GAME_MODES_DIR">
+<!--   -->
+</a><code>public&nbsp;static&nbsp;final&nbsp;java.lang.String</code></td>
+<td><code><a href="Arbiter.html#GAME_MODES_DIR">GAME_MODES_DIR</a></code></td>
+<td class="colLast"><code>"game-modes"</code></td>
+</tr>
+<tr class="rowColor">
 <td class="colFirst"><a name="Arbiter.MAPS_DIR">
 <!--   -->
 </a><code>public&nbsp;static&nbsp;final&nbsp;java.lang.String</code></td>
 <td><code><a href="Arbiter.html#MAPS_DIR">MAPS_DIR</a></code></td>
 <td class="colFirst"><a name="Arbiter.MAPS_DIR">
 <!--   -->
 </a><code>public&nbsp;static&nbsp;final&nbsp;java.lang.String</code></td>
 <td><code><a href="Arbiter.html#MAPS_DIR">MAPS_DIR</a></code></td>
-<td class="colLast"><code>"maps-reclaimed"</code></td>
+<td class="colLast"><code>"maps"</code></td>
+</tr>
+<tr class="altColor">
+<td class="colFirst"><a name="Arbiter.OUTPUT_DIR">
+<!--   -->
+</a><code>public&nbsp;static&nbsp;final&nbsp;java.lang.String</code></td>
+<td><code><a href="Arbiter.html#OUTPUT_DIR">OUTPUT_DIR</a></code></td>
+<td class="colLast"><code>"halo-3-reclaimed-files"</code></td>
 </tr>
 <tr class="rowColor">
 <td class="colFirst"><a name="Arbiter.SCREENSHOTS_DIR">
 <!--   -->
 </a><code>public&nbsp;static&nbsp;final&nbsp;java.lang.String</code></td>
 <td><code><a href="Arbiter.html#SCREENSHOTS_DIR">SCREENSHOTS_DIR</a></code></td>
 </tr>
 <tr class="rowColor">
 <td class="colFirst"><a name="Arbiter.SCREENSHOTS_DIR">
 <!--   -->
 </a><code>public&nbsp;static&nbsp;final&nbsp;java.lang.String</code></td>
 <td><code><a href="Arbiter.html#SCREENSHOTS_DIR">SCREENSHOTS_DIR</a></code></td>
-<td class="colLast"><code>"screenshots-reclaimed"</code></td>
+<td class="colLast"><code>"screenshots"</code></td>
 </tr>
 </tbody>
 </table>
 </tr>
 </tbody>
 </table>
 <!--   -->
 </a><code>public&nbsp;static&nbsp;final&nbsp;java.lang.String</code></td>
 <td><code><a href="ErrorHandler.html#BAD_OUTPUT">BAD_OUTPUT</a></code></td>
 <!--   -->
 </a><code>public&nbsp;static&nbsp;final&nbsp;java.lang.String</code></td>
 <td><code><a href="ErrorHandler.html#BAD_OUTPUT">BAD_OUTPUT</a></code></td>
-<td class="colLast"><code>"123: Output directory problem"</code></td>
+<td class="colLast"><code>"124: Output directory problem"</code></td>
 </tr>
 <tr class="altColor">
 <td class="colFirst"><a name="ErrorHandler.CANT_PROCESS">
 </tr>
 <tr class="altColor">
 <td class="colFirst"><a name="ErrorHandler.CANT_PROCESS">
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index dd1a2bd99be53e670c35a534b820701e9c393963..3eeed72e1f1f9e959694e9d084ed8d9aa29a1105 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Deprecated List (Halo 3 Reclaimer)</title>
 <title>Deprecated List (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 1bde3bc88eabc331a8eeb20d1491a7474a3cf61b..55ff8af8a6d8beef8573188876c961967420f646 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>API Help (Halo 3 Reclaimer)</title>
 <title>API Help (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 447e6e5b53432fc404bc5bc13f6e1d3e1fc49bc1..55c380af4cbaba4b7fa074962464b7f9f446a431 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Index (Halo 3 Reclaimer)</title>
 <title>Index (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
@@ -70,7 +70,7 @@
 <!--   -->
 </a></div>
 <!-- ========= END OF TOP NAVBAR ========= -->
 <!--   -->
 </a></div>
 <!-- ========= END OF TOP NAVBAR ========= -->
-<div class="contentContainer"><a href="#I:A">A</a>&nbsp;<a href="#I:B">B</a>&nbsp;<a href="#I:C">C</a>&nbsp;<a href="#I:D">D</a>&nbsp;<a href="#I:E">E</a>&nbsp;<a href="#I:F">F</a>&nbsp;<a href="#I:G">G</a>&nbsp;<a href="#I:M">M</a>&nbsp;<a href="#I:N">N</a>&nbsp;<a href="#I:P">P</a>&nbsp;<a href="#I:S">S</a>&nbsp;<a href="#I:T">T</a>&nbsp;<a href="#I:U">U</a>&nbsp;<a name="I:A">
+<div class="contentContainer"><a href="#I:A">A</a>&nbsp;<a href="#I:B">B</a>&nbsp;<a href="#I:C">C</a>&nbsp;<a href="#I:D">D</a>&nbsp;<a href="#I:E">E</a>&nbsp;<a href="#I:F">F</a>&nbsp;<a href="#I:G">G</a>&nbsp;<a href="#I:M">M</a>&nbsp;<a href="#I:N">N</a>&nbsp;<a href="#I:O">O</a>&nbsp;<a href="#I:P">P</a>&nbsp;<a href="#I:S">S</a>&nbsp;<a href="#I:T">T</a>&nbsp;<a href="#I:U">U</a>&nbsp;<a name="I:A">
 <!--   -->
 </a>
 <h2 class="title">A</h2>
 <!--   -->
 </a>
 <h2 class="title">A</h2>
 <dd>
 <div class="block">TUI only: print error AND QUIT</div>
 </dd>
 <dd>
 <div class="block">TUI only: print error AND QUIT</div>
 </dd>
+<dt><span class="memberNameLink"><a href="GameModeReclaimer.html#extractGameMode-java.io.File-java.io.File-">extractGameMode(File, File)</a></span> - Method in class <a href="GameModeReclaimer.html" title="class in &lt;Unnamed&gt;">GameModeReclaimer</a></dt>
+<dd>
+<div class="block">Extracts the game mode from an Xbox 360 file and saves it, together with
+ the package it came in, under its in-game name in the output directory.</div>
+</dd>
 <dt><span class="memberNameLink"><a href="MapReclaimer.html#extractMap-java.io.File-java.io.File-">extractMap(File, File)</a></span> - Method in class <a href="MapReclaimer.html" title="class in &lt;Unnamed&gt;">MapReclaimer</a></dt>
 <dd>
 <dt><span class="memberNameLink"><a href="MapReclaimer.html#extractMap-java.io.File-java.io.File-">extractMap(File, File)</a></span> - Method in class <a href="MapReclaimer.html" title="class in &lt;Unnamed&gt;">MapReclaimer</a></dt>
 <dd>
-<div class="block">Extracts the Forge map from an Xbox 360 file and saves it as
- &lt;input name&gt;-reclaimed.map in the output directory.</div>
+<div class="block">Extracts the Forge map from an Xbox 360 file and saves it, together with
+ the package it came in, under the map's in-game name in the output directory.</div>
 </dd>
 <dt><span class="memberNameLink"><a href="ScreenshotReclaimer.html#extractScreenshot-java.io.File-java.io.File-">extractScreenshot(File, File)</a></span> - Method in class <a href="ScreenshotReclaimer.html" title="class in &lt;Unnamed&gt;">ScreenshotReclaimer</a></dt>
 <dd>
 </dd>
 <dt><span class="memberNameLink"><a href="ScreenshotReclaimer.html#extractScreenshot-java.io.File-java.io.File-">extractScreenshot(File, File)</a></span> - Method in class <a href="ScreenshotReclaimer.html" title="class in &lt;Unnamed&gt;">ScreenshotReclaimer</a></dt>
 <dd>
 </a>
 <h2 class="title">G</h2>
 <dl>
 </a>
 <h2 class="title">G</h2>
 <dl>
+<dt><span class="memberNameLink"><a href="Arbiter.html#GAME_MODES_DIR">GAME_MODES_DIR</a></span> - Static variable in class <a href="Arbiter.html" title="class in &lt;Unnamed&gt;">Arbiter</a></dt>
+<dd>&nbsp;</dd>
+<dt><a href="GameModeReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">GameModeReclaimer</span></a> - Class in <a href="package-summary.html">&lt;Unnamed&gt;</a></dt>
+<dd>
+<div class="block">Core extraction engine for custom game types (game modes).</div>
+</dd>
+<dt><span class="memberNameLink"><a href="GameModeReclaimer.html#GameModeReclaimer--">GameModeReclaimer()</a></span> - Constructor for class <a href="GameModeReclaimer.html" title="class in &lt;Unnamed&gt;">GameModeReclaimer</a></dt>
+<dd>&nbsp;</dd>
 <dt><a href="GraphicalUI.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">GraphicalUI</span></a> - Class in <a href="package-summary.html">&lt;Unnamed&gt;</a></dt>
 <dd>
 <div class="block">Graphical User Interface for Halo 3 Reclaimer.</div>
 <dt><a href="GraphicalUI.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">GraphicalUI</span></a> - Class in <a href="package-summary.html">&lt;Unnamed&gt;</a></dt>
 <dd>
 <div class="block">Graphical User Interface for Halo 3 Reclaimer.</div>
 <dt><span class="memberNameLink"><a href="ErrorHandler.html#NOT_READY">NOT_READY</a></span> - Static variable in class <a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;">ErrorHandler</a></dt>
 <dd>&nbsp;</dd>
 </dl>
 <dt><span class="memberNameLink"><a href="ErrorHandler.html#NOT_READY">NOT_READY</a></span> - Static variable in class <a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;">ErrorHandler</a></dt>
 <dd>&nbsp;</dd>
 </dl>
+<a name="I:O">
+<!--   -->
+</a>
+<h2 class="title">O</h2>
+<dl>
+<dt><span class="memberNameLink"><a href="Arbiter.html#OUTPUT_DIR">OUTPUT_DIR</a></span> - Static variable in class <a href="Arbiter.html" title="class in &lt;Unnamed&gt;">Arbiter</a></dt>
+<dd>&nbsp;</dd>
+</dl>
 <a name="I:P">
 <!--   -->
 </a>
 <a name="I:P">
 <!--   -->
 </a>
 <dl>
 <dt><span class="memberNameLink"><a href="Arbiter.html#processFile-java.io.File-java.io.File-">processFile(File, File)</a></span> - Static method in class <a href="Arbiter.html" title="class in &lt;Unnamed&gt;">Arbiter</a></dt>
 <dd>
 <dl>
 <dt><span class="memberNameLink"><a href="Arbiter.html#processFile-java.io.File-java.io.File-">processFile(File, File)</a></span> - Static method in class <a href="Arbiter.html" title="class in &lt;Unnamed&gt;">Arbiter</a></dt>
 <dd>
-<div class="block">Reclaims all screenshots and maps found in a file or folder.</div>
+<div class="block">Reclaims all screenshots, maps and game modes found in a file or folder.</div>
 </dd>
 <dt><span class="memberNameLink"><a href="TerminalUI.html#processFile-java.lang.String:A-">processFile(String[])</a></span> - Static method in class <a href="TerminalUI.html" title="class in &lt;Unnamed&gt;">TerminalUI</a></dt>
 <dd>
 </dd>
 <dt><span class="memberNameLink"><a href="TerminalUI.html#processFile-java.lang.String:A-">processFile(String[])</a></span> - Static method in class <a href="TerminalUI.html" title="class in &lt;Unnamed&gt;">TerminalUI</a></dt>
 <dd>
 <dt><span class="memberNameLink"><a href="ErrorHandler.html#UNKNOWN_FILE">UNKNOWN_FILE</a></span> - Static variable in class <a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;">ErrorHandler</a></dt>
 <dd>&nbsp;</dd>
 </dl>
 <dt><span class="memberNameLink"><a href="ErrorHandler.html#UNKNOWN_FILE">UNKNOWN_FILE</a></span> - Static variable in class <a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;">ErrorHandler</a></dt>
 <dd>&nbsp;</dd>
 </dl>
-<a href="#I:A">A</a>&nbsp;<a href="#I:B">B</a>&nbsp;<a href="#I:C">C</a>&nbsp;<a href="#I:D">D</a>&nbsp;<a href="#I:E">E</a>&nbsp;<a href="#I:F">F</a>&nbsp;<a href="#I:G">G</a>&nbsp;<a href="#I:M">M</a>&nbsp;<a href="#I:N">N</a>&nbsp;<a href="#I:P">P</a>&nbsp;<a href="#I:S">S</a>&nbsp;<a href="#I:T">T</a>&nbsp;<a href="#I:U">U</a>&nbsp;</div>
+<a href="#I:A">A</a>&nbsp;<a href="#I:B">B</a>&nbsp;<a href="#I:C">C</a>&nbsp;<a href="#I:D">D</a>&nbsp;<a href="#I:E">E</a>&nbsp;<a href="#I:F">F</a>&nbsp;<a href="#I:G">G</a>&nbsp;<a href="#I:M">M</a>&nbsp;<a href="#I:N">N</a>&nbsp;<a href="#I:O">O</a>&nbsp;<a href="#I:P">P</a>&nbsp;<a href="#I:S">S</a>&nbsp;<a href="#I:T">T</a>&nbsp;<a href="#I:U">U</a>&nbsp;</div>
 <!-- ======= START OF BOTTOM NAVBAR ====== -->
 <div class="bottomNav"><a name="navbar.bottom">
 <!--   -->
 <!-- ======= START OF BOTTOM NAVBAR ====== -->
 <div class="bottomNav"><a name="navbar.bottom">
 <!--   -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 37a8f0c06092b1978f5df12241b3f8a7a01b64e5..c7527a98c98d8d60eab48d0cb95a2d304c332268 100644 (file)
@@ -2,7 +2,7 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Halo 3 Reclaimer</title>
 <script type="text/javascript">
     tmpTargetPage = "" + window.location.search;
 <title>Halo 3 Reclaimer</title>
 <script type="text/javascript">
     tmpTargetPage = "" + window.location.search;
index 5890b6392f608167ab79ec4977da6f626bf32e45..b85b094cc8ff1237f57398cf37ec09da8b2efe45 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Overview (Halo 3 Reclaimer)</title>
 <title>Overview (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
@@ -71,7 +71,7 @@
 </a></div>
 <!-- ========= END OF TOP NAVBAR ========= -->
 <div class="header">
 </a></div>
 <!-- ========= END OF TOP NAVBAR ========= -->
 <div class="header">
-<h1 class="title">Halo 3 Reclaimer - File extractor from Xbox 360 version of Halo 3</h1>
+<h1 class="title">Halo 3 Reclaimer - player data extractor from Xbox 360 version of Halo 3</h1>
 </div>
 <div class="contentContainer">
 <table class="overviewSummary" border="0" cellpadding="3" cellspacing="0" summary="Packages table, listing packages, and an explanation">
 </div>
 <div class="contentContainer">
 <table class="overviewSummary" border="0" cellpadding="3" cellspacing="0" summary="Packages table, listing packages, and an explanation">
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index e0bc245b81544245adafbb39f143d10d124bc805..614e4b4a434a31462d2bd50ff96f824074f01e4f 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Class Hierarchy (Halo 3 Reclaimer)</title>
 <title>Class Hierarchy (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 </li>
 <li type="circle"><a href="Cortana.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Cortana</span></a></li>
 <li type="circle"><a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">ErrorHandler</span></a></li>
 </li>
 <li type="circle"><a href="Cortana.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Cortana</span></a></li>
 <li type="circle"><a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">ErrorHandler</span></a></li>
+<li type="circle"><a href="GameModeReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">GameModeReclaimer</span></a></li>
 <li type="circle"><a href="Main.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Main</span></a></li>
 <li type="circle"><a href="MapReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">MapReclaimer</span></a></li>
 <li type="circle"><a href="ScreenshotReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">ScreenshotReclaimer</span></a></li>
 <li type="circle"><a href="Main.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Main</span></a></li>
 <li type="circle"><a href="MapReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">MapReclaimer</span></a></li>
 <li type="circle"><a href="ScreenshotReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">ScreenshotReclaimer</span></a></li>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 962c54bb0c08dbff3bf0c278923520e15a909ffc..218ed58d667bf1155f1070d1700ec140d54299c6 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>&lt;Unnamed&gt; (Halo 3 Reclaimer)</title>
 <title>&lt;Unnamed&gt; (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
@@ -16,6 +16,7 @@
 <li><a href="Arbiter.html" title="class in &lt;Unnamed&gt;" target="classFrame">Arbiter</a></li>
 <li><a href="Cortana.html" title="class in &lt;Unnamed&gt;" target="classFrame">Cortana</a></li>
 <li><a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;" target="classFrame">ErrorHandler</a></li>
 <li><a href="Arbiter.html" title="class in &lt;Unnamed&gt;" target="classFrame">Arbiter</a></li>
 <li><a href="Cortana.html" title="class in &lt;Unnamed&gt;" target="classFrame">Cortana</a></li>
 <li><a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;" target="classFrame">ErrorHandler</a></li>
+<li><a href="GameModeReclaimer.html" title="class in &lt;Unnamed&gt;" target="classFrame">GameModeReclaimer</a></li>
 <li><a href="GraphicalUI.html" title="class in &lt;Unnamed&gt;" target="classFrame">GraphicalUI</a></li>
 <li><a href="Main.html" title="class in &lt;Unnamed&gt;" target="classFrame">Main</a></li>
 <li><a href="MapReclaimer.html" title="class in &lt;Unnamed&gt;" target="classFrame">MapReclaimer</a></li>
 <li><a href="GraphicalUI.html" title="class in &lt;Unnamed&gt;" target="classFrame">GraphicalUI</a></li>
 <li><a href="Main.html" title="class in &lt;Unnamed&gt;" target="classFrame">Main</a></li>
 <li><a href="MapReclaimer.html" title="class in &lt;Unnamed&gt;" target="classFrame">MapReclaimer</a></li>
index e8c9a87ecbaef56cdd78a8b4d5751252b0ee7bf1..6bc8ce11d2020cc26bcea42448eeeea59e39c7e8 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title> (Halo 3 Reclaimer)</title>
 <title> (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 </td>
 </tr>
 <tr class="rowColor">
 </td>
 </tr>
 <tr class="rowColor">
+<td class="colFirst"><a href="GameModeReclaimer.html" title="class in &lt;Unnamed&gt;">GameModeReclaimer</a></td>
+<td class="colLast">
+<div class="block">Core extraction engine for custom game types (game modes).</div>
+</td>
+</tr>
+<tr class="altColor">
 <td class="colFirst"><a href="GraphicalUI.html" title="class in &lt;Unnamed&gt;">GraphicalUI</a></td>
 <td class="colLast">
 <div class="block">Graphical User Interface for Halo 3 Reclaimer.</div>
 </td>
 </tr>
 <td class="colFirst"><a href="GraphicalUI.html" title="class in &lt;Unnamed&gt;">GraphicalUI</a></td>
 <td class="colLast">
 <div class="block">Graphical User Interface for Halo 3 Reclaimer.</div>
 </td>
 </tr>
-<tr class="altColor">
+<tr class="rowColor">
 <td class="colFirst"><a href="Main.html" title="class in &lt;Unnamed&gt;">Main</a></td>
 <td class="colLast">
 <div class="block">Main entry point for the program.</div>
 </td>
 </tr>
 <td class="colFirst"><a href="Main.html" title="class in &lt;Unnamed&gt;">Main</a></td>
 <td class="colLast">
 <div class="block">Main entry point for the program.</div>
 </td>
 </tr>
-<tr class="rowColor">
+<tr class="altColor">
 <td class="colFirst"><a href="MapReclaimer.html" title="class in &lt;Unnamed&gt;">MapReclaimer</a></td>
 <td class="colLast">
 <div class="block">Core extraction engine for Forge map variants.</div>
 </td>
 </tr>
 <td class="colFirst"><a href="MapReclaimer.html" title="class in &lt;Unnamed&gt;">MapReclaimer</a></td>
 <td class="colLast">
 <div class="block">Core extraction engine for Forge map variants.</div>
 </td>
 </tr>
-<tr class="altColor">
+<tr class="rowColor">
 <td class="colFirst"><a href="ScreenshotReclaimer.html" title="class in &lt;Unnamed&gt;">ScreenshotReclaimer</a></td>
 <td class="colLast">
 <div class="block">Core extraction engine for screenshots.</div>
 </td>
 </tr>
 <td class="colFirst"><a href="ScreenshotReclaimer.html" title="class in &lt;Unnamed&gt;">ScreenshotReclaimer</a></td>
 <td class="colLast">
 <div class="block">Core extraction engine for screenshots.</div>
 </td>
 </tr>
-<tr class="rowColor">
+<tr class="altColor">
 <td class="colFirst"><a href="TerminalUI.html" title="class in &lt;Unnamed&gt;">TerminalUI</a></td>
 <td class="colLast">
 <div class="block">Terminal User Interface for Halo 3 Reclaimer.</div>
 <td class="colFirst"><a href="TerminalUI.html" title="class in &lt;Unnamed&gt;">TerminalUI</a></td>
 <td class="colLast">
 <div class="block">Terminal User Interface for Halo 3 Reclaimer.</div>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index e1b8c80dd58dbe51184c4fae1ccc90c441088633..846fb69a7e9499f87a45a56a06af03cc3a5ae185 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title> Class Hierarchy (Halo 3 Reclaimer)</title>
 <title> Class Hierarchy (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 </li>
 <li type="circle"><a href="Cortana.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Cortana</span></a></li>
 <li type="circle"><a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">ErrorHandler</span></a></li>
 </li>
 <li type="circle"><a href="Cortana.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Cortana</span></a></li>
 <li type="circle"><a href="ErrorHandler.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">ErrorHandler</span></a></li>
+<li type="circle"><a href="GameModeReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">GameModeReclaimer</span></a></li>
 <li type="circle"><a href="Main.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Main</span></a></li>
 <li type="circle"><a href="MapReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">MapReclaimer</span></a></li>
 <li type="circle"><a href="ScreenshotReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">ScreenshotReclaimer</span></a></li>
 <li type="circle"><a href="Main.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">Main</span></a></li>
 <li type="circle"><a href="MapReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">MapReclaimer</span></a></li>
 <li type="circle"><a href="ScreenshotReclaimer.html" title="class in &lt;Unnamed&gt;"><span class="typeNameLink">ScreenshotReclaimer</span></a></li>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 72967b08109e582b1e9d9d722b560705f311f9c9..970a9801e31563054eb67c2f8af1ea8cd3b9bfe8 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Uses of Package  (Halo 3 Reclaimer)</title>
 <title>Uses of Package  (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 98c2f06ecb3920ede340952fadb1e2fb0092fe2a..eb5ad0398e99689781c950190cd854ab8120a7cd 100644 (file)
@@ -2,9 +2,9 @@
 <!-- NewPage -->
 <html lang="en">
 <head>
 <!-- NewPage -->
 <html lang="en">
 <head>
-<!-- Generated by javadoc (1.8.0_504) on Mon Nov 02 17:45:23 CET 2026 -->
+<!-- Generated by javadoc (1.8.0_504) on Tue Nov 03 11:00:11 CET 2026 -->
 <title>Serialized Form (Halo 3 Reclaimer)</title>
 <title>Serialized Form (Halo 3 Reclaimer)</title>
-<meta name="date" content="2026-11-02">
+<meta name="date" content="2026-11-03">
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
 <script type="text/javascript" src="script.js"></script>
 </head>
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
 <!--   -->
 </a></div>
 <!-- ======== END OF BOTTOM NAVBAR ======= -->
-<p class="legalCopy"><small>Copyright 2025. Documentation licensed under GFDL v1.3.</small></p>
+<p class="legalCopy"><small>Copyright 2025, 2026. Documentation licensed under GFDL v1.3.</small></p>
 </body>
 </html>
 </body>
 </html>
index 9bcb026b8884efed1316449c8b029db39c6f61ff..f0c1ad9ef2d505a75cb18afbb08b41750658e4e7 100644 (file)
Binary files a/doc/user-manual-editable.odt and b/doc/user-manual-editable.odt differ
index 4a98f7ccb0e8b11ad168b21cbda7abbf0c0f6629..81f8e4c155f3597b0b8e01afc5320327e3089131 100644 (file)
Binary files a/doc/user-manual.pdf and b/doc/user-manual.pdf differ
index 990df864af721065a4f3c02acfd0655caccc141b..12ddbc3b645f9bbf73c9eb40c7280fde2e92d8e5 100644 (file)
Binary files a/halo3-reclaimer.jar and b/halo3-reclaimer.jar differ
index b260dc4a9ed0584cfdfbd0fda9e2e16446d9fcbd..8f3ed6ed48f731ff8c648e090ee547a53eabf9d8 100644 (file)
@@ -8,10 +8,13 @@
 import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Locale;
+import java.util.Map;
 
 /**
 * Arbiter class that determines file type and routes to appropriate extractor.
 
 /**
 * Arbiter class that determines file type and routes to appropriate extractor.
@@ -21,24 +24,31 @@ import java.util.List;
 * Cortana first, which copies the Halo 3 files out of the Xbox file system;
 * other files on the stick are ignored then.</p>
 *
 * Cortana first, which copies the Halo 3 files out of the Xbox file system;
 * other files on the stick are ignored then.</p>
 *
-* <p>Halo 3 keeps every screenshot and every Forge map in its own package,
-* so the Arbiter looks inside each file: a package holding
+* <p>Halo 3 keeps every screenshot, Forge map and custom game type in its own
+* package, so the Arbiter looks inside each file: a package holding
 * <code>screen.shot</code> is a screenshot, one holding <code>sandbox.map</code>
 * <code>screen.shot</code> is a screenshot, one holding <code>sandbox.map</code>
-* is a map. Screenshots are checked first, then maps. Anything else is skipped.
-* Only when the content says nothing (DDS, TGA, raw textures) the file name
-* decides, like in version 1.0.</p>
+* is a map and one holding <code>variant.slayer</code>, <code>variant.ctf</code>...
+* is a game mode. Screenshots are checked first, then maps, then game modes.
+* Anything else is skipped. Only when the content says nothing (DDS, TGA,
+* raw textures) the file name decides, like in version 1.0.</p>
 *
 *
-* <p>Results go to two folders in the output directory:
-* <code>screenshots-reclaimed</code> and <code>maps-reclaimed</code>.</p>
+* <p>Results go to <code>halo-3-reclaimed-files</code> in the output directory,
+* sorted into <code>screenshots</code>, <code>maps</code> and <code>game-modes</code>.
+* Files are named after their in-game names, see {@link #saveFiles}.</p>
 *
 *
-* @version 1.1
+* @version 1.2
 */
 
 public class Arbiter {
 
 */
 
 public class Arbiter {
 
-    // folders created inside the output directory
-    public static final String SCREENSHOTS_DIR = "screenshots-reclaimed";
-    public static final String MAPS_DIR = "maps-reclaimed";
+    // everything goes into one folder inside the output directory, sorted by kind
+    public static final String OUTPUT_DIR = "halo-3-reclaimed-files";
+    public static final String SCREENSHOTS_DIR = "screenshots";
+    public static final String MAPS_DIR = "maps";
+    public static final String GAME_MODES_DIR = "game-modes";
+
+    // output folders of older test builds, never read back either
+    private static final String[] OLD_OUTPUT_DIRS = { "screenshots-reclaimed", "maps-reclaimed" };
 
     // the file inside a package that holds a screenshot
     private static final String SCREENSHOT_FILE = "screen.shot";
 
     // the file inside a package that holds a screenshot
     private static final String SCREENSHOT_FILE = "screen.shot";
@@ -49,12 +59,12 @@ public class Arbiter {
     // how deep to look into sub folders
     private static final int MAX_FOLDER_DEPTH = 8;
 
     // how deep to look into sub folders
     private static final int MAX_FOLDER_DEPTH = 8;
 
-    private enum Kind { SCREENSHOT, MAP, OTHER }
+    private enum Kind { SCREENSHOT, MAP, GAME_MODE, OTHER }
 
     /**
 
     /**
-    * Reclaims all screenshots and maps found in a file or folder.
+    * Reclaims all screenshots, maps and game modes found in a file or folder.
     *
     *
-    * @return what was reclaimed, for example "Reclaimed 2 pictures and 1 map"
+    * @return what was reclaimed, for example "Reclaimed 2 pictures, 1 map and 3 game modes"
     * @throws Exception if nothing could be reclaimed
     */
     public static String processFile(File input, File outputDir) throws Exception {
     * @throws Exception if nothing could be reclaimed
     */
     public static String processFile(File input, File outputDir) throws Exception {
@@ -78,13 +88,15 @@ public class Arbiter {
     }
 
     /**
     }
 
     /**
-    * Sorts the files out and runs the right reclaimer on each, screenshots first.
+    * Sorts the files out and runs the right reclaimer on each: screenshots first,
+    * then maps, then game modes.
     * folder: there are many files to go through, root: where console paths start.
     */
     private static String reclaim(List<File> files, File outputDir, boolean folder, File root) throws Exception {
     * folder: there are many files to go through, root: where console paths start.
     */
     private static String reclaim(List<File> files, File outputDir, boolean folder, File root) throws Exception {
-        // sort everything out first, screenshots go first
+        // sort everything out first
         List<File> screenshots = new ArrayList<File>();
         List<File> maps = new ArrayList<File>();
         List<File> screenshots = new ArrayList<File>();
         List<File> maps = new ArrayList<File>();
+        List<File> gameModes = new ArrayList<File>();
         String skipReason = null;
         for (File file : files) {
             StringBuilder reason = new StringBuilder();
         String skipReason = null;
         for (File file : files) {
             StringBuilder reason = new StringBuilder();
@@ -94,6 +106,8 @@ public class Arbiter {
                 screenshots.add(file);
             } else if (kind == Kind.MAP) {
                 maps.add(file);
                 screenshots.add(file);
             } else if (kind == Kind.MAP) {
                 maps.add(file);
+            } else if (kind == Kind.GAME_MODE) {
+                gameModes.add(file);
             } else {
                 // short reason for the status bar, the detail only goes to the console
                 skipReason = reason.toString();
             } else {
                 // short reason for the status bar, the detail only goes to the console
                 skipReason = reason.toString();
@@ -106,23 +120,24 @@ public class Arbiter {
             }
         }
 
             }
         }
 
-        if (screenshots.isEmpty() && maps.isEmpty()) {
+        if (screenshots.isEmpty() && maps.isEmpty() && gameModes.isEmpty()) {
             if (folder) {
                 throw ErrorHandler.makeException(ErrorHandler.UNKNOWN_FILE + ": no Halo 3 files found");
             }
             throw ErrorHandler.makeException(skipReason);
         }
         if (folder) {
             if (folder) {
                 throw ErrorHandler.makeException(ErrorHandler.UNKNOWN_FILE + ": no Halo 3 files found");
             }
             throw ErrorHandler.makeException(skipReason);
         }
         if (folder) {
-            System.out.println("Found " + count(screenshots.size(), "screenshot")
-                    + " and " + count(maps.size(), "map"));
+            System.out.println("Found " + list(count(screenshots.size(), "screenshot"),
+                    count(maps.size(), "map"), count(gameModes.size(), "game mode")));
         }
 
         }
 
+        File reclaimedDir = new File(outputDir, OUTPUT_DIR);
         int failed = 0;
         Exception lastError = null;
 
         int pictures = 0;
         if (!screenshots.isEmpty()) {
         int failed = 0;
         Exception lastError = null;
 
         int pictures = 0;
         if (!screenshots.isEmpty()) {
-            File screenshotsDir = outputFolder(outputDir, SCREENSHOTS_DIR);
+            File screenshotsDir = outputFolder(reclaimedDir, SCREENSHOTS_DIR);
             for (File file : screenshots) {
                 System.out.println("Detected as screenshot: " + shown(root, file));
                 try {
             for (File file : screenshots) {
                 System.out.println("Detected as screenshot: " + shown(root, file));
                 try {
@@ -138,7 +153,7 @@ public class Arbiter {
 
         int reclaimedMaps = 0;
         if (!maps.isEmpty()) {
 
         int reclaimedMaps = 0;
         if (!maps.isEmpty()) {
-            File mapsDir = outputFolder(outputDir, MAPS_DIR);
+            File mapsDir = outputFolder(reclaimedDir, MAPS_DIR);
             for (File file : maps) {
                 System.out.println("Detected as Forge map: " + shown(root, file));
                 try {
             for (File file : maps) {
                 System.out.println("Detected as Forge map: " + shown(root, file));
                 try {
@@ -152,17 +167,33 @@ public class Arbiter {
             }
         }
 
             }
         }
 
-        if (pictures + reclaimedMaps == 0) {
+        int reclaimedGameModes = 0;
+        if (!gameModes.isEmpty()) {
+            File gameModesDir = outputFolder(reclaimedDir, GAME_MODES_DIR);
+            for (File file : gameModes) {
+                System.out.println("Detected as game mode: " + shown(root, file));
+                try {
+                    new GameModeReclaimer().extractGameMode(file, gameModesDir);
+                    reclaimedGameModes++;
+                } catch (Exception e) {
+                    failed++;
+                    lastError = e;
+                    System.out.println("Could not reclaim " + shown(root, file) + ": " + e.getMessage());
+                }
+            }
+        }
+
+        if (pictures + reclaimedMaps + reclaimedGameModes == 0) {
             if (failed == 1) {
                 throw lastError;
             }
             throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": all " + failed + " files failed");
         }
 
             if (failed == 1) {
                 throw lastError;
             }
             throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": all " + failed + " files failed");
         }
 
-        List<String> reclaimed = new ArrayList<String>();
-        if (pictures > 0) reclaimed.add(count(pictures, "picture"));
-        if (reclaimedMaps > 0) reclaimed.add(count(reclaimedMaps, "map"));
-        String result = "Reclaimed " + String.join(" and ", reclaimed);
+        String result = "Reclaimed " + list(
+                pictures > 0 ? count(pictures, "picture") : null,
+                reclaimedMaps > 0 ? count(reclaimedMaps, "map") : null,
+                reclaimedGameModes > 0 ? count(reclaimedGameModes, "game mode") : null);
         if (failed > 0) {
             result += " (" + failed + " failed)";
         }
         if (failed > 0) {
             result += " (" + failed + " failed)";
         }
@@ -170,14 +201,15 @@ public class Arbiter {
     }
 
     /**
     }
 
     /**
-    * Tells what a file is by looking inside it. Screenshots are checked first.
-    * When the file is neither, a short error goes into reason and, when
-    * there is more to say, an explanation for the console into detail.
+    * Tells what a file is by looking inside it. Screenshots are checked first,
+    * then maps, then game modes. When the file is none of them, a short error
+    * goes into reason and, when there is more to say, an explanation for the
+    * console into detail.
     */
     private static Kind detect(File file, StringBuilder reason, StringBuilder detail) {
         if (file.length() > MAX_FILE_SIZE) {
             reason.append(ErrorHandler.CANT_PROCESS + ": file too big");
     */
     private static Kind detect(File file, StringBuilder reason, StringBuilder detail) {
         if (file.length() > MAX_FILE_SIZE) {
             reason.append(ErrorHandler.CANT_PROCESS + ": file too big");
-            detail.append(file.length() + " bytes, too big for a screenshot or map");
+            detail.append(file.length() + " bytes, too big for a Halo 3 save");
             return Kind.OTHER;
         }
 
             return Kind.OTHER;
         }
 
@@ -194,8 +226,9 @@ public class Arbiter {
                 List<String> names = MapReclaimer.listPackageFiles(data);
                 if (containsName(names, SCREENSHOT_FILE)) return Kind.SCREENSHOT;
                 if (containsName(names, MapReclaimer.MAP_VARIANT_FILE)) return Kind.MAP;
                 List<String> names = MapReclaimer.listPackageFiles(data);
                 if (containsName(names, SCREENSHOT_FILE)) return Kind.SCREENSHOT;
                 if (containsName(names, MapReclaimer.MAP_VARIANT_FILE)) return Kind.MAP;
-                // game types, films...
-                reason.append(ErrorHandler.CANT_PROCESS + ": not a screenshot or map");
+                if (GameModeReclaimer.findGameModeFile(names) != null) return Kind.GAME_MODE;
+                // films, campaign saves...
+                reason.append(ErrorHandler.CANT_PROCESS + ": not a screenshot, map or game mode");
                 detail.append(names.isEmpty() ? "the package is empty"
                         : "the package holds " + String.join(", ", names));
                 return Kind.OTHER;
                 detail.append(names.isEmpty() ? "the package is empty"
                         : "the package holds " + String.join(", ", names));
                 return Kind.OTHER;
@@ -204,13 +237,14 @@ public class Arbiter {
                 reason.append(e.getMessage() != null ? e.getMessage() : ErrorHandler.CANT_PROCESS);
             }
         } else if (MapReclaimer.isBLFFile(data)) {
                 reason.append(e.getMessage() != null ? e.getMessage() : ErrorHandler.CANT_PROCESS);
             }
         } else if (MapReclaimer.isBLFFile(data)) {
-            // screen.shot or sandbox.map already taken out of its package
+            // screen.shot, sandbox.map or variant.* already taken out of its package
             int fileType = MapReclaimer.readBlfFileType(data);
             if (fileType == MapReclaimer.FILE_TYPE_SCREENSHOT) return Kind.SCREENSHOT;
             if (fileType == MapReclaimer.FILE_TYPE_MAP_VARIANT) return Kind.MAP;
             int fileType = MapReclaimer.readBlfFileType(data);
             if (fileType == MapReclaimer.FILE_TYPE_SCREENSHOT) return Kind.SCREENSHOT;
             if (fileType == MapReclaimer.FILE_TYPE_MAP_VARIANT) return Kind.MAP;
+            if (GameModeReclaimer.isGameModeType(fileType)) return Kind.GAME_MODE;
             if (fileType >= 0) {
             if (fileType >= 0) {
-                reason.append(ErrorHandler.CANT_PROCESS + ": not a screenshot or map");
-                detail.append("Halo file of type " + fileType + " (game type, film...)");
+                reason.append(ErrorHandler.CANT_PROCESS + ": not a screenshot, map or game mode");
+                detail.append("Halo file of type " + fileType + " (film, campaign save...)");
                 return Kind.OTHER;
             }
         }
                 return Kind.OTHER;
             }
         }
@@ -241,6 +275,85 @@ public class Arbiter {
         return false;
     }
 
         return false;
     }
 
+    /**
+    * Saves reclaimed files under their in-game name: all lowercase, spaces as
+    * underscores, e.g. <code>guardian_infection.map</code> and
+    * <code>guardian_infection.package</code>. When a different file already uses
+    * that name, a number is added (<code>guardian_infection_2.map</code>); the very
+    * same file reclaimed again just keeps its name, so running the reclaimer twice
+    * doesn't make copies.
+    *
+    * @param inGameName name stored in the file, can be empty
+    * @param fallback   used when the in-game name is empty, e.g. the package name
+    * @param files      extension (with the dot) and contents of each file to save
+    * @return the name used, without extension
+    * @throws Exception if a file can't be written
+    */
+    static String saveFiles(File folder, String inGameName, String fallback, Map<String, byte[]> files) throws Exception {
+        String base = toFileName(inGameName);
+        if (base.isEmpty()) base = toFileName(fallback);
+        if (base.isEmpty()) base = "unnamed";
+
+        for (int number = 1; number < 10000; number++) {
+            String name = (number == 1) ? base : base + "_" + number;
+            if (isFree(folder, name, files)) {
+                for (Map.Entry<String, byte[]> file : files.entrySet()) {
+                    File target = new File(folder, name + file.getKey());
+                    writeFile(target, file.getValue());
+                    System.out.println("Saved as: " + target.getPath());
+                }
+                return name;
+            }
+        }
+        System.out.println("Too many files named " + base + " in " + folder.getPath());
+        throw ErrorHandler.makeException(ErrorHandler.BAD_OUTPUT + ": too many same names");
+    }
+
+    /**
+    * In-game name to file name: lowercase, spaces become underscores, and so does
+    * everything Windows doesn't allow in file names.
+    */
+    static String toFileName(String name) {
+        if (name == null) return "";
+        String lower = name.trim().toLowerCase(Locale.ROOT);
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < lower.length(); i++) {
+            char c = lower.charAt(i);
+            boolean forbidden = c == ' ' || Character.isISOControl(c) || "\\/:*?\"<>|".indexOf(c) >= 0;
+            sb.append(forbidden ? '_' : c);
+        }
+        String fileName = sb.toString();
+        // Windows also refuses names ending in a dot and a few device names like "con"
+        if (fileName.endsWith(".")) fileName += "_";
+        if (fileName.matches("con|prn|aux|nul|com[1-9]|lpt[1-9]")) fileName += "_";
+        return fileName;
+    }
+
+    // true when each of these files is new, or already there with exactly the same contents
+    private static boolean isFree(File folder, String name, Map<String, byte[]> files) {
+        for (Map.Entry<String, byte[]> file : files.entrySet()) {
+            File existing = new File(folder, name + file.getKey());
+            if (!existing.exists()) continue;
+            try {
+                if (!existing.isFile() || existing.length() != file.getValue().length
+                        || !Arrays.equals(readFile(existing), file.getValue())) {
+                    return false;
+                }
+            } catch (IOException e) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static void writeFile(File file, byte[] data) throws Exception {
+        try (FileOutputStream out = new FileOutputStream(file)) {
+            out.write(data);
+        } catch (IOException e) {
+            throw ErrorHandler.makeException(ErrorHandler.BAD_OUTPUT + ": " + e.getMessage());
+        }
+    }
+
     private static List<File> listFiles(File folder) {
         List<File> files = new ArrayList<File>();
         collectFiles(folder, files, 0);
     private static List<File> listFiles(File folder) {
         List<File> files = new ArrayList<File>();
         collectFiles(folder, files, 0);
@@ -267,8 +380,7 @@ public class Arbiter {
         Arrays.sort(children);
         for (File child : children) {
             if (child.isDirectory()) {
         Arrays.sort(children);
         for (File child : children) {
             if (child.isDirectory()) {
-                String name = child.getName();
-                if (!name.equals(SCREENSHOTS_DIR) && !name.equals(MAPS_DIR)) {
+                if (!isOutputFolder(child.getName())) {
                     collectFiles(child, files, depth + 1);
                 }
             } else if (child.isFile()) {
                     collectFiles(child, files, depth + 1);
                 }
             } else if (child.isFile()) {
@@ -277,6 +389,14 @@ public class Arbiter {
         }
     }
 
         }
     }
 
+    private static boolean isOutputFolder(String name) {
+        if (name.equals(OUTPUT_DIR)) return true;
+        for (String old : OLD_OUTPUT_DIRS) {
+            if (name.equals(old)) return true;
+        }
+        return false;
+    }
+
     /**
     * Returns the output sub folder, creates it when needed.
     */
     /**
     * Returns the output sub folder, creates it when needed.
     */
@@ -300,4 +420,14 @@ public class Arbiter {
     private static String count(int number, String word) {
         return number + " " + word + (number == 1 ? "" : "s");
     }
     private static String count(int number, String word) {
         return number + " " + word + (number == 1 ? "" : "s");
     }
+
+    // "a", "a and b", "a, b and c"; empty parts are left out
+    private static String list(String... parts) {
+        List<String> used = new ArrayList<String>();
+        for (String part : parts) {
+            if (part != null) used.add(part);
+        }
+        if (used.size() <= 1) return used.isEmpty() ? "" : used.get(0);
+        return String.join(", ", used.subList(0, used.size() - 1)) + " and " + used.get(used.size() - 1);
+    }
 }
 }
index 37789c7e00a807e7feea605e004d614dcbb11482..64cdd1726ae8626a817558cc302fc2f0857d7260 100644 (file)
 * 
 * <p>This class just stores error messages and prints them when needed.</p>
 * 
 * 
 * <p>This class just stores error messages and prints them when needed.</p>
 * 
-* @version 1.0
+* @version 1.1
 */
 public class ErrorHandler {
     
     // Error messages
     public static final String UNKNOWN_FILE = "343: Not a Halo game (file)";
     public static final String TOO_MANY_ARGS = "002: Too many arguments";
 */
 public class ErrorHandler {
     
     // Error messages
     public static final String UNKNOWN_FILE = "343: Not a Halo game (file)";
     public static final String TOO_MANY_ARGS = "002: Too many arguments";
-    public static final String BAD_INPUT = "123: Input file problem";
-    public static final String BAD_OUTPUT = "123: Output directory problem";
+    public static final String BAD_INPUT = "011: Input file problem";
+    public static final String BAD_OUTPUT = "012: Output directory problem";
     public static final String CANT_PROCESS = "003: Cannot process file";
     public static final String NOT_READY = "117: Feature not ready yet";
     public static final String NEED_SELECTION = "001: Please select file and directory";
     public static final String CANT_PROCESS = "003: Cannot process file";
     public static final String NOT_READY = "117: Feature not ready yet";
     public static final String NEED_SELECTION = "001: Please select file and directory";
@@ -46,4 +46,4 @@ public class ErrorHandler {
     public static Exception makeException(String errorMessage) {
         return new Exception(errorMessage);
     }
     public static Exception makeException(String errorMessage) {
         return new Exception(errorMessage);
     }
-}
\ No newline at end of file
+}
diff --git a/src/GameModeReclaimer.java b/src/GameModeReclaimer.java
new file mode 100644 (file)
index 0000000..34276ed
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+* Halo 3 Reclaimer [GameModeReclaimer.java]
+* player data extractor from Xbox 360 version of Halo 3
+* copyright 2026, David Polakovic, licensed under GPLv3
+* contact: email@dpolakovic.space
+*/
+
+import java.io.File;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+* Core extraction engine for custom game types (game modes).
+*
+* <p>Halo 3 saves every custom game type as its own Xbox 360 package, just
+* like the Forge maps. The package holds one file named after the game's
+* rules, for example <code>variant.slayer</code> or <code>variant.ctf</code>.
+* That file is the game mode itself, all of its settings, stored in Bungie's
+* BLF format. The package and BLF readers are shared with MapReclaimer.</p>
+*
+* <p>Supported input formats:</p>
+* <ul>
+*   <li>STFS container files holding a Halo 3 game type</li>
+*   <li>already extracted <code>variant.*</code> files (BLF)</li>
+* </ul>
+*
+* <p>Two files are saved, named after the game mode (see Arbiter.saveFiles):</p>
+* <ul>
+*   <li><code>team_snipers.slayer</code>: byte for byte the file the console
+*       keeps inside the package, its ending tells the game type</li>
+*   <li><code>team_snipers.package</code>: the untouched package, which tools
+*       like Horizon or Velocity can sign for another player's profile
+*       (only when the input was a package)</li>
+* </ul>
+*
+* @version 1.0
+*/
+
+public class GameModeReclaimer {
+
+    // the file inside the package is called variant.<game type>
+    private static final String GAME_MODE_FILE_PREFIX = "variant";
+
+    // saved game file types 1 to 9 are the game modes, in this order
+    private static final int FIRST_GAME_MODE_TYPE = 1;
+    private static final int LAST_GAME_MODE_TYPE = 9;
+    private static final String[] GAME_MODE_NAMES = {
+        null, "Capture the Flag", "Slayer", "Oddball", "King of the Hill",
+        "Juggernaut", "Territories", "Assault", "Infection", "VIP"
+    };
+
+    // file endings for bare files whose name doesn't tell (packages always do)
+    private static final String[] GAME_MODE_EXTENSIONS = {
+        null, "ctf", "slayer", "oddball", "king", "juggernaut",
+        "territories", "assault", "infection", "vip"
+    };
+
+    /**
+    * Extracts the game mode from an Xbox 360 file and saves it, together with
+    * the package it came in, under its in-game name in the output directory.
+    *
+    * @throws Exception if the file cannot be read, is not a Halo 3 game mode,
+    *                   or is damaged
+    */
+    public void extractGameMode(File inputFile, File outputDir) throws Exception {
+        byte[] fileData = MapReclaimer.readInputFile(inputFile);
+
+        System.out.println("File size: " + fileData.length + " bytes");
+
+        byte[] variantData;
+        byte[] packageData = null;
+        String extension;
+        MapReclaimer.ContentInfo info;
+        try {
+            if (MapReclaimer.isSTFSFile(fileData)) {
+                System.out.println("Detected STFS file format");
+                String variantFile = findGameModeFile(MapReclaimer.listPackageFiles(fileData));
+                variantData = (variantFile == null) ? null : MapReclaimer.readPackageFile(fileData, variantFile);
+                if (variantData == null) {
+                    throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": no game mode inside");
+                }
+                extension = extensionOf(variantFile);
+                packageData = fileData;
+            } else if (MapReclaimer.isBLFFile(fileData)) {
+                System.out.println("Detected extracted game mode (BLF) file");
+                variantData = fileData;
+                extension = extensionOf(inputFile.getName());
+            } else {
+                System.out.println("Not an Xbox 360 package or game mode");
+                throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": not a game mode");
+            }
+            info = readGameModeInfo(variantData);
+        } catch (RuntimeException e) {
+            // a bug or a file layout nobody has seen yet, keep the GUI informed
+            e.printStackTrace();
+            throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": damaged or unsupported file");
+        }
+
+        if (extension == null) {
+            extension = GAME_MODE_EXTENSIONS[info.fileType];
+        }
+
+        System.out.println("Game mode: \"" + info.name + "\" (" + GAME_MODE_NAMES[info.fileType] + ")");
+        if (!info.description.isEmpty()) {
+            System.out.println("Description: " + info.description);
+        }
+        if (!info.author.isEmpty()) {
+            System.out.println("Author: " + info.author);
+        }
+        System.out.println("Game mode size: " + variantData.length + " bytes");
+
+        // the game mode itself, and the package it came in, ready to be signed for another player
+        Map<String, byte[]> files = new LinkedHashMap<String, byte[]>();
+        files.put("." + extension, variantData);
+        if (packageData != null) {
+            files.put(MapReclaimer.PACKAGE_EXTENSION, packageData);
+        } else {
+            System.out.println("No package to save, the input was a bare game mode file");
+        }
+        Arbiter.saveFiles(outputDir, info.name, MapReclaimer.baseName(inputFile), files);
+    }
+
+    /**
+    * The game mode file among the files of a package (variant.slayer,
+    * variant.ctf...), or null if there is none.
+    */
+    static String findGameModeFile(List<String> names) {
+        for (String name : names) {
+            if (name.toLowerCase(Locale.ROOT).startsWith(GAME_MODE_FILE_PREFIX)) return name;
+        }
+        return null;
+    }
+
+    /**
+    * True for the saved game file types of game modes (1 to 9).
+    */
+    static boolean isGameModeType(int fileType) {
+        return fileType >= FIRST_GAME_MODE_TYPE && fileType <= LAST_GAME_MODE_TYPE;
+    }
+
+    /**
+    * Makes sure the BLF file really is a complete game mode and reads its metadata.
+    */
+    private MapReclaimer.ContentInfo readGameModeInfo(byte[] blf) throws Exception {
+        if (!MapReclaimer.isBLFFile(blf)) {
+            System.out.println("The game mode file is not a BLF file");
+            throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": not a game mode");
+        }
+
+        MapReclaimer.ContentInfo info = MapReclaimer.readContentInfo(blf);
+        if (info == null) {
+            System.out.println("Game mode has a broken chunk");
+            throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": damaged game mode");
+        }
+        if (!info.complete) {
+            System.out.println("Game mode is incomplete, its end of file chunk is missing");
+            throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": damaged game mode");
+        }
+        if (!isGameModeType(info.fileType)) {
+            System.out.println("This Halo file is of type " + info.fileType + ", not a game mode");
+            throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": not a game mode");
+        }
+        return info;
+    }
+
+    // "variant.slayer" -> "slayer", null when the name doesn't tell
+    private static String extensionOf(String fileName) {
+        String lower = fileName.toLowerCase(Locale.ROOT);
+        if (!lower.startsWith(GAME_MODE_FILE_PREFIX + ".")) return null;
+        String extension = Arbiter.toFileName(lower.substring(GAME_MODE_FILE_PREFIX.length() + 1));
+        return extension.isEmpty() ? null : extension;
+    }
+}
index 08112f6c675803f52c3818400af2c21b1c75f6a8..3c39f790fa8560674ddea3269a500744bd8bbfa5 100644 (file)
@@ -17,7 +17,7 @@ import java.io.File;
 * <p>This class contains all the GUI components and 
 * logic for the application's windowed interface</p>
 * 
 * <p>This class contains all the GUI components and 
 * logic for the application's windowed interface</p>
 * 
-* @version 1.1
+* @version 1.2
 */
 
 public class GraphicalUI extends JFrame {
 */
 
 public class GraphicalUI extends JFrame {
@@ -161,7 +161,7 @@ public class GraphicalUI extends JFrame {
     */
     private void showVersionInfo() {
         String versionText = "Halo 3 Reclaimer\n\n" +
     */
     private void showVersionInfo() {
         String versionText = "Halo 3 Reclaimer\n\n" +
-                           "Version: 0.2\n\n" +
+                           "Version: 0.3\n\n" +
                            "player data extractor from Xbox 360 version of Halo 3\n" +
                            "copyright 2025, 2026, David Polakovic, licensed under GPLv3\n";
         
                            "player data extractor from Xbox 360 version of Halo 3\n" +
                            "copyright 2025, 2026, David Polakovic, licensed under GPLv3\n";
         
@@ -182,7 +182,8 @@ public class GraphicalUI extends JFrame {
                         "The tool will extract your user data from the\n" +
                        "Xbox 360 save. Currently supports:\n" +
                        "-screenshots\n" +
                         "The tool will extract your user data from the\n" +
                        "Xbox 360 save. Currently supports:\n" +
                        "-screenshots\n" +
-                       "-Forge maps\n\n";
+                       "-Forge map variants\n" +
+                       "-Custom game types\n\n";
         
         JOptionPane.showMessageDialog(this, helpText, "Help", 
                                     JOptionPane.INFORMATION_MESSAGE);
         
         JOptionPane.showMessageDialog(this, helpText, "Help", 
                                     JOptionPane.INFORMATION_MESSAGE);
index 58f68f3445f1c6f49a93b7c2894c7f4faefdd3ec..29db9512eccbdec59f13b52e8dd10f638bdad9dd 100644 (file)
@@ -50,4 +50,4 @@ public class Main {
             GraphicalUI.main();
         }
     }
             GraphicalUI.main();
         }
     }
-}
\ No newline at end of file
+}
index 9a136c670048fc6826454647cb81b7bb1c3c6577..7823c2e6780e5afc29a5c7fccbddf4b3e93889db 100644 (file)
@@ -8,13 +8,14 @@
 import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
+import java.util.Map;
 
 /**
 * Core extraction engine for Forge map variants.
 
 /**
 * Core extraction engine for Forge map variants.
@@ -29,7 +30,8 @@ import java.util.List;
 * <p>Unlike screenshots, the map has no magic bytes worth scanning for,
 * so this class reads the STFS file system properly: it walks the file
 * table, follows the block chain of <code>sandbox.map</code> and checks
 * <p>Unlike screenshots, the map has no magic bytes worth scanning for,
 * so this class reads the STFS file system properly: it walks the file
 * table, follows the block chain of <code>sandbox.map</code> and checks
-* each block against the package hash tables.</p>
+* each block against the package hash tables. These package and BLF readers
+* are shared with the other reclaimers.</p>
 *
 * <p>Supported input formats:</p>
 * <ul>
 *
 * <p>Supported input formats:</p>
 * <ul>
@@ -37,10 +39,16 @@ import java.util.List;
 *   <li>already extracted <code>sandbox.map</code> files (BLF)</li>
 * </ul>
 *
 *   <li>already extracted <code>sandbox.map</code> files (BLF)</li>
 * </ul>
 *
-* <p>The saved file is not converted in any way. It is byte for byte the
-* <code>sandbox.map</code> the console keeps inside the package.</p>
+* <p>Two files are saved, named after the map (see Arbiter.saveFiles):</p>
+* <ul>
+*   <li><code>guardian_infection.map</code>: byte for byte the
+*       <code>sandbox.map</code> the console keeps inside the package</li>
+*   <li><code>guardian_infection.package</code>: the untouched package, which
+*       tools like Horizon or Velocity can sign for another player's profile
+*       (only when the input was a package)</li>
+* </ul>
 *
 *
-* @version 1.0
+* @version 1.1
 */
 
 public class MapReclaimer {
 */
 
 public class MapReclaimer {
@@ -64,6 +72,9 @@ public class MapReclaimer {
     // the file inside the package that holds the Forge map
     static final String MAP_VARIANT_FILE = "sandbox.map";
 
     // the file inside the package that holds the Forge map
     static final String MAP_VARIANT_FILE = "sandbox.map";
 
+    // packages are saved untouched under this extension
+    static final String PACKAGE_EXTENSION = ".package";
+
     // BLF format constants (the format of sandbox.map)
     private static final int BLF_START = 0x5F626C66; // "_blf"
     private static final int BLF_END = 0x5F656F66; // "_eof"
     // BLF format constants (the format of sandbox.map)
     private static final int BLF_START = 0x5F626C66; // "_blf"
     private static final int BLF_END = 0x5F656F66; // "_eof"
@@ -92,23 +103,29 @@ public class MapReclaimer {
     };
 
     /**
     };
 
     /**
-    * Extracts the Forge map from an Xbox 360 file and saves it as
-    * &lt;input name&gt;-reclaimed.map in the output directory.
+    * Extracts the Forge map from an Xbox 360 file and saves it, together with
+    * the package it came in, under the map's in-game name in the output directory.
     *
     * @throws Exception if the file cannot be read, is not a Halo 3 Forge map,
     *                   or is damaged
     */
     public void extractMap(File inputFile, File outputDir) throws Exception {
     *
     * @throws Exception if the file cannot be read, is not a Halo 3 Forge map,
     *                   or is damaged
     */
     public void extractMap(File inputFile, File outputDir) throws Exception {
-        byte[] fileData = readFile(inputFile);
+        byte[] fileData = readInputFile(inputFile);
 
         System.out.println("File size: " + fileData.length + " bytes");
 
         byte[] mapData;
 
         System.out.println("File size: " + fileData.length + " bytes");
 
         byte[] mapData;
-        MapInfo info;
+        byte[] packageData = null;
+        ContentInfo info;
         try {
             if (isSTFSFile(fileData)) {
                 System.out.println("Detected STFS file format");
         try {
             if (isSTFSFile(fileData)) {
                 System.out.println("Detected STFS file format");
-                mapData = extractFromSTFS(fileData);
+                mapData = readPackageFile(fileData, MAP_VARIANT_FILE);
+                if (mapData == null) {
+                    // the "Package contains" lines tell what is in there instead
+                    throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": no Forge map inside");
+                }
+                packageData = fileData;
             } else if (isBLFFile(fileData)) {
                 System.out.println("Detected extracted map variant (BLF) file");
                 mapData = fileData;
             } else if (isBLFFile(fileData)) {
                 System.out.println("Detected extracted map variant (BLF) file");
                 mapData = fileData;
@@ -135,26 +152,25 @@ public class MapReclaimer {
         }
         System.out.println("Map variant size: " + mapData.length + " bytes");
 
         }
         System.out.println("Map variant size: " + mapData.length + " bytes");
 
-        // generate output filename, same scheme as the screenshots
-        String baseName = inputFile.getName();
-        int dotIndex = baseName.lastIndexOf('.');
-        if (dotIndex > 0) {
-            baseName = baseName.substring(0, dotIndex);
+        // the map itself, and the package it came in, ready to be signed for another player
+        Map<String, byte[]> files = new LinkedHashMap<String, byte[]>();
+        files.put(".map", mapData);
+        if (packageData != null) {
+            files.put(PACKAGE_EXTENSION, packageData);
+        } else {
+            System.out.println("No package to save, the input was a bare map variant");
         }
         }
-        String outputFileName = baseName + "-reclaimed.map";
-        File outputFile = new File(outputDir, outputFileName);
-
-        writeFile(outputFile, mapData);
-        System.out.println("Saved as: " + outputFile.getAbsolutePath());
+        Arbiter.saveFiles(outputDir, info.name, baseName(inputFile), files);
     }
 
     /**
     }
 
     /**
-    * Reads the entire contents of a file into a byte array.
+    * Reads the entire contents of an input file into a byte array.
+    * Halo 3 packages are small, anything huge is refused.
     */
     */
-    private byte[] readFile(File file) throws Exception {
+    static byte[] readInputFile(File file) throws Exception {
         long length = file.length();
         if (length > MAX_INPUT_SIZE) {
         long length = file.length();
         if (length > MAX_INPUT_SIZE) {
-            throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": file too big for a Forge map");
+            throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": file too big");
         }
 
         byte[] data = new byte[(int) length];
         }
 
         byte[] data = new byte[(int) length];
@@ -166,12 +182,13 @@ public class MapReclaimer {
         return data;
     }
 
         return data;
     }
 
-    private void writeFile(File file, byte[] data) throws Exception {
-        try (FileOutputStream out = new FileOutputStream(file)) {
-            out.write(data);
-        } catch (IOException e) {
-            throw ErrorHandler.makeException(ErrorHandler.BAD_OUTPUT + ": " + e.getMessage());
-        }
+    /**
+    * The file name without its extension, the fallback when there is no in-game name.
+    */
+    static String baseName(File file) {
+        String name = file.getName();
+        int dotIndex = name.lastIndexOf('.');
+        return (dotIndex > 0) ? name.substring(0, dotIndex) : name;
     }
 
     static boolean isSTFSFile(byte[] data) {
     }
 
     static boolean isSTFSFile(byte[] data) {
@@ -186,7 +203,7 @@ public class MapReclaimer {
 
     /**
     * Lists the files inside a Halo 3 package. The Arbiter uses this to tell
 
     /**
     * Lists the files inside a Halo 3 package. The Arbiter uses this to tell
-    * screenshots (screen.shot) from maps (sandbox.map) without trusting file names.
+    * screenshots, maps and game modes apart without trusting file names.
     *
     * @throws Exception if the package is damaged or belongs to another game
     */
     *
     * @throws Exception if the package is damaged or belongs to another game
     */
@@ -206,23 +223,12 @@ public class MapReclaimer {
     }
 
     /**
     }
 
     /**
-    * Saved game file type of an already extracted BLF file
-    * (FILE_TYPE_SCREENSHOT, FILE_TYPE_MAP_VARIANT...), or -1 if it can't be told.
-    */
-    static int readBlfFileType(byte[] blf) {
-        if (!isBLFFile(blf)) return -1;
-        int[] chunks = findBlfChunks(blf);
-        if (chunks == null) return -1;
-        if (hasMetadata(blf, chunks[0])) {
-            return readInt32(blf, chunks[0] + METADATA_OFFSET + METADATA_FILE_TYPE);
-        }
-        return chunks[1] >= 0 ? FILE_TYPE_MAP_VARIANT : -1;
-    }
-
-    /**
-    * Finds sandbox.map in the package and returns its contents.
+    * Reads one file out of a Halo 3 package and prints what the package holds.
+    *
+    * @return the file contents, or null when the package has no file of that name
+    * @throws Exception if the package is damaged or belongs to another game
     */
     */
-    private byte[] extractFromSTFS(byte[] data) throws Exception {
+    static byte[] readPackageFile(byte[] data, String fileName) throws Exception {
         StfsPackage stfs = new StfsPackage(data);
 
         System.out.printf("Package: \"%s\", title ID %08X%n", stfs.displayName, stfs.titleId);
         StfsPackage stfs = new StfsPackage(data);
 
         System.out.printf("Package: \"%s\", title ID %08X%n", stfs.displayName, stfs.titleId);
@@ -230,76 +236,106 @@ public class MapReclaimer {
             throw ErrorHandler.makeException(ErrorHandler.UNKNOWN_FILE + ": not a Halo 3 package");
         }
 
             throw ErrorHandler.makeException(ErrorHandler.UNKNOWN_FILE + ": not a Halo 3 package");
         }
 
-        StfsEntry mapEntry = null;
+        StfsEntry wanted = null;
         for (StfsEntry entry : stfs.entries) {
             System.out.println("Package contains: " + entry.name
                     + (entry.isDirectory ? " (folder)" : " (" + entry.size + " bytes)"));
         for (StfsEntry entry : stfs.entries) {
             System.out.println("Package contains: " + entry.name
                     + (entry.isDirectory ? " (folder)" : " (" + entry.size + " bytes)"));
-            if (mapEntry == null && !entry.isDirectory && entry.name.equalsIgnoreCase(MAP_VARIANT_FILE)) {
-                mapEntry = entry;
+            if (wanted == null && !entry.isDirectory && entry.name.equalsIgnoreCase(fileName)) {
+                wanted = entry;
             }
         }
             }
         }
+        if (wanted == null) return null;
 
 
-        if (mapEntry == null) {
-            // the "Package contains" lines above tell what is in there instead
-            throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": no Forge map inside");
-        }
-
-        byte[] mapData = stfs.readFile(mapEntry);
+        byte[] content = stfs.readFile(wanted);
 
         if (stfs.badBlocks > 0) {
 
         if (stfs.badBlocks > 0) {
-            System.out.println("Warning: " + stfs.badBlocks + " block(s) of " + MAP_VARIANT_FILE
+            System.out.println("Warning: " + stfs.badBlocks + " block(s) of " + wanted.name
                     + " do not match the package hashes, the package may be damaged");
         } else {
             System.out.println("Package hashes OK");
         }
                     + " do not match the package hashes, the package may be damaged");
         } else {
             System.out.println("Package hashes OK");
         }
-        return mapData;
+        return content;
+    }
+
+    /**
+    * Saved game file type of an already extracted BLF file
+    * (FILE_TYPE_SCREENSHOT, FILE_TYPE_MAP_VARIANT...), or -1 if it can't be told.
+    */
+    static int readBlfFileType(byte[] blf) {
+        ContentInfo info = readContentInfo(blf);
+        if (info == null) return -1;
+        if (info.fileType >= 0) return info.fileType;
+        return info.hasMapVariant ? FILE_TYPE_MAP_VARIANT : -1;
+    }
+
+    /**
+    * What a saved Halo 3 file (map, game mode, screenshot) says about itself.
+    */
+    static final class ContentInfo {
+        String name = "";
+        String description = "";
+        String author = "";
+        int fileType = -1;
+        int mapId = -1;
+        boolean complete; // it ends with its _eof chunk
+        boolean hasMapVariant; // it has an mvar chunk
     }
 
     /**
     }
 
     /**
-    * Walks the BLF chunks of a map variant, makes sure it really is a Forge map
-    * and reads its name, description and author.
+    * Reads the metadata of a BLF file: name, description, author, file type...
+    *
+    * @return the metadata, or null if it is not a BLF file or a chunk is broken
     */
     */
-    private MapInfo readMapInfo(byte[] blf) throws Exception {
+    static ContentInfo readContentInfo(byte[] blf) {
+        if (!isBLFFile(blf)) return null;
+        int[] chunks = findBlfChunks(blf);
+        if (chunks == null) return null;
+
+        ContentInfo info = new ContentInfo();
+        info.hasMapVariant = chunks[1] >= 0;
+        info.complete = chunks[2] >= 0;
+
+        // both chdr and mvar start with the same metadata block, chdr is the official one
+        int metadata = -1;
+        if (hasMetadata(blf, chunks[0])) {
+            metadata = chunks[0] + METADATA_OFFSET;
+        } else if (hasMetadata(blf, chunks[1])) {
+            metadata = chunks[1] + METADATA_OFFSET;
+        }
+        if (metadata >= 0) {
+            info.name = readUtf16(blf, metadata + METADATA_NAME, 32);
+            info.description = readUtf8(blf, metadata + METADATA_DESCRIPTION, 128);
+            info.author = readUtf8(blf, metadata + METADATA_AUTHOR, 16);
+            info.fileType = readInt32(blf, metadata + METADATA_FILE_TYPE);
+            info.mapId = readInt32(blf, metadata + METADATA_MAP_ID);
+        }
+        return info;
+    }
+
+    /**
+    * Makes sure the BLF file really is a complete Forge map and reads its metadata.
+    */
+    private ContentInfo readMapInfo(byte[] blf) throws Exception {
         if (!isBLFFile(blf)) {
             System.out.println(MAP_VARIANT_FILE + " is not a map variant (BLF) file");
             throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": not a Forge map");
         }
 
         if (!isBLFFile(blf)) {
             System.out.println(MAP_VARIANT_FILE + " is not a map variant (BLF) file");
             throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": not a Forge map");
         }
 
-        int[] chunks = findBlfChunks(blf);
-        if (chunks == null) {
+        ContentInfo info = readContentInfo(blf);
+        if (info == null) {
             System.out.println("Map variant has a broken chunk");
             throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": damaged map variant");
         }
             System.out.println("Map variant has a broken chunk");
             throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": damaged map variant");
         }
-        int contentHeader = chunks[0];
-        int mapVariant = chunks[1];
-        if (chunks[2] < 0) {
+        if (!info.complete) {
             System.out.println("Map variant is incomplete, its end of file chunk is missing");
             throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": damaged map variant");
         }
             System.out.println("Map variant is incomplete, its end of file chunk is missing");
             throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": damaged map variant");
         }
-
-        // both chdr and mvar start with the same metadata block, chdr is the official one
-        int metadata = -1;
-        if (hasMetadata(blf, contentHeader)) {
-            metadata = contentHeader + METADATA_OFFSET;
-        } else if (hasMetadata(blf, mapVariant)) {
-            metadata = mapVariant + METADATA_OFFSET;
-        }
-
-        int fileType = metadata >= 0 ? readInt32(blf, metadata + METADATA_FILE_TYPE) : -1;
-        if (mapVariant < 0 && fileType != FILE_TYPE_MAP_VARIANT) {
-            if (fileType >= 0 && fileType < FILE_TYPE_NAMES.length) {
-                System.out.println("This file is a " + FILE_TYPE_NAMES[fileType]);
+        if (!info.hasMapVariant && info.fileType != FILE_TYPE_MAP_VARIANT) {
+            if (info.fileType >= 0 && info.fileType < FILE_TYPE_NAMES.length) {
+                System.out.println("This file is a " + FILE_TYPE_NAMES[info.fileType]);
             }
             throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": not a Forge map");
         }
             }
             throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS + ": not a Forge map");
         }
-
-        MapInfo info = new MapInfo();
-        if (metadata >= 0) {
-            info.name = readUtf16(blf, metadata + METADATA_NAME, 32);
-            info.description = readUtf8(blf, metadata + METADATA_DESCRIPTION, 128);
-            info.author = readUtf8(blf, metadata + METADATA_AUTHOR, 16);
-            info.mapId = readInt32(blf, metadata + METADATA_MAP_ID);
-        }
         return info;
     }
 
         return info;
     }
 
@@ -343,16 +379,6 @@ public class MapReclaimer {
         return chunkSize >= METADATA_OFFSET + METADATA_MAP_ID + 4;
     }
 
         return chunkSize >= METADATA_OFFSET + METADATA_MAP_ID + 4;
     }
 
-    /**
-    * What the map variant says about itself.
-    */
-    private static final class MapInfo {
-        String name = "";
-        String description = "";
-        String author = "";
-        int mapId = -1;
-    }
-
     /**
     * One entry of the STFS file table.
     */
     /**
     * One entry of the STFS file table.
     */
index 18b8428c8c75f81fdcb3969d73659293dcf355d6..abdf6a4ed0d3257265f35d71bafcc2afe0a3561f 100644 (file)
@@ -1,7 +1,7 @@
 /*
 * Halo 3 Reclaimer [ScreenshotReclaimer.java]
 * player data extractor from Xbox 360 version of Halo 3 
 /*
 * Halo 3 Reclaimer [ScreenshotReclaimer.java]
 * player data extractor from Xbox 360 version of Halo 3 
-* copyright 2025, David Polakovic, licensed under GPLv3
+* copyright 2025, 2026, David Polakovic, licensed under GPLv3
 * contact: email@dpolakovic.space
 */
 
 * contact: email@dpolakovic.space
 */
 
@@ -10,6 +10,8 @@ import java.awt.image.BufferedImage;
 import java.io.*;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.io.*;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.util.LinkedHashMap;
+import java.util.Map;
 
 /**
 * Core extraction engine for screenshots.
 
 /**
 * Core extraction engine for screenshots.
@@ -33,7 +35,7 @@ import java.nio.ByteOrder;
 *   <li>Standard RGBA and RGB formats</li>
 * </ul>
 * 
 *   <li>Standard RGBA and RGB formats</li>
 * </ul>
 * 
-* @version 1.0
+* @version 1.1
 */
 
 public class ScreenshotReclaimer {
 */
 
 public class ScreenshotReclaimer {
@@ -56,7 +58,10 @@ public class ScreenshotReclaimer {
     private static final int STFS_MAGIC_CON = 0x434F4E20; // "CON "
     private static final int STFS_MAGIC_LIVE = 0x4C495645; // "LIVE"
     private static final int STFS_MAGIC_PIRS = 0x50495253; // "PIRS"
     private static final int STFS_MAGIC_CON = 0x434F4E20; // "CON "
     private static final int STFS_MAGIC_LIVE = 0x4C495645; // "LIVE"
     private static final int STFS_MAGIC_PIRS = 0x50495253; // "PIRS"
-    
+
+    // the file inside a package that holds the screenshot and its name
+    private static final String SCREENSHOT_FILE = "screen.shot";
+
     /**
     * Extracts a screenshot from an Xbox 360 file and saves it as a JPEG.
     * 
     /**
     * Extracts a screenshot from an Xbox 360 file and saves it as a JPEG.
     * 
@@ -101,19 +106,32 @@ public class ScreenshotReclaimer {
         }
         
         System.out.println("Successfully extracted image: " + image.getWidth() + "x" + image.getHeight());
         }
         
         System.out.println("Successfully extracted image: " + image.getWidth() + "x" + image.getHeight());
-        
-        // generate output filename
-        String baseName = inputFile.getName();
-        int dotIndex = baseName.lastIndexOf('.');
-        if (dotIndex > 0) {
-            baseName = baseName.substring(0, dotIndex);
-        }
-        String outputFileName = baseName + "-reclaimed.jpg";
-        File outputFile = new File(outputDir, outputFileName);
 
 
-        if (!ImageIO.write(image, "jpg", outputFile)) {
+        // plain JPEG, named after the screenshot's in-game name like the maps and game modes
+        ByteArrayOutputStream jpeg = new ByteArrayOutputStream();
+        if (!ImageIO.write(image, "jpg", jpeg)) {
             throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS);
         }
             throw ErrorHandler.makeException(ErrorHandler.CANT_PROCESS);
         }
+        Map<String, byte[]> files = new LinkedHashMap<String, byte[]>();
+        files.put(".jpg", jpeg.toByteArray());
+        Arbiter.saveFiles(outputDir, readScreenshotName(fileData), MapReclaimer.baseName(inputFile), files);
+    }
+
+    /**
+    * The in-game name of the screenshot, read from its package, or "" if unknown.
+    */
+    private String readScreenshotName(byte[] data) {
+        try {
+            byte[] shot = isSTFSFile(data) ? MapReclaimer.readPackageFile(data, SCREENSHOT_FILE) : data;
+            MapReclaimer.ContentInfo info = (shot == null) ? null : MapReclaimer.readContentInfo(shot);
+            if (info != null && !info.name.isEmpty()) {
+                System.out.println("Screenshot: \"" + info.name + "\"");
+                return info.name;
+            }
+        } catch (Exception e) {
+            // the picture is what counts, the name is a bonus
+        }
+        return "";
     }
     
     /**
     }
     
     /**
@@ -706,4 +724,4 @@ public class ScreenshotReclaimer {
         
         return hasNonBlack && hasNonWhite;
     }
         
         return hasNonBlack && hasNonWhite;
     }
-}
\ No newline at end of file
+}
index 015adad1e699d111f8baa45c6641bcf693c0e920..8e909a017c19cfdef3b2bce7b28d05084d9f577e 100644 (file)
@@ -12,15 +12,15 @@ import java.io.File;
 * 
 * <p>This class contains all the command-line interface logic.</p>
 * 
 * 
 * <p>This class contains all the command-line interface logic.</p>
 * 
-* @version 1.1
+* @version 1.2
 */
 public class TerminalUI {
     
     public static void showVersion() {
         System.out.println("Halo 3 Reclaimer");
 */
 public class TerminalUI {
     
     public static void showVersion() {
         System.out.println("Halo 3 Reclaimer");
-        System.out.println("Version: 1.0");
+        System.out.println("Version: 0.3");
         System.out.println("player data extractor from Xbox 360 version of Halo 3");
         System.out.println("player data extractor from Xbox 360 version of Halo 3");
-        System.out.println("copyright 2025, David Polakovic, licensed under GPLv3");
+        System.out.println("copyright 2025, 2026, David Polakovic, licensed under GPLv3");
     }
     
 
     }
     
 
@@ -91,7 +91,8 @@ public class TerminalUI {
             // use Arbiter to process the file or folder
             String result = Arbiter.processFile(inputFile, outputDir);
 
             // use Arbiter to process the file or folder
             String result = Arbiter.processFile(inputFile, outputDir);
 
-            System.out.println(result + " into: " + outputDir.getAbsolutePath());
+            System.out.println(result + " into: "
+                    + new File(outputDir, Arbiter.OUTPUT_DIR).getAbsolutePath());
 
         } catch (Exception e) {
             // parse error code from exception message if it starts with a number
 
         } catch (Exception e) {
             // parse error code from exception message if it starts with a number
Copyright 2022-2026 David Polakovic. Individual project licenses are located in project root in full length.

Site generated using Gitweb. Read the documentation for JavaScript and cookie information. Additional source code available here under GPLv3 license.

Server for this subdomain is RFC 2324 compliant